天天看點

【Spring】AOP詳解 底層原理及操作術語 五種通知類型(使用AspectJ 注解及XML操作)一、AOP概念二、AOP底層原理三、AOP術語四、AOP操作

【Spring系列】IOC控制反轉

【Spring系列】IOC操作bean管理(一)——bean管理執行個體詳解

【Spring系列】IOC操作bean管理(二)——bean的生命周期、作用域

【Spring系列】IOC操作bean管理(三)——xml自動裝配

【Spring系列】IOC操作bean管理(四)——引入外部屬性檔案

【Spring系列】IOC操作bean管理(五)——bean管理注解

【Spring系列】AOP詳解

【Spring系列】JdbcTemplate操作資料庫詳解

【Spring系列】spring事務配置詳解

【Spring系列】spring5架構新特性

文章目錄

  • 一、AOP概念
  • 二、AOP底層原理
    • (一)動态代理
      • (1)動态代理有兩種情況
        • 第一種:有接口情況,使用JDK動态代理
        • 第二種:沒有接口情況,使用CGLIB動态代理
      • (2)AOP使用JDK動态代理
  • 三、AOP術語
    • (1)連接配接點
    • (2)切入點
    • (3)增強(通知)
    • (4)切面
  • 四、AOP操作
    • (1)AspectJ介紹
      • 附加:切入點表達式
    • (2)基于注解方式使用AspectJ的aop操作
      • 第一步:導入AspectJ相關的jar包
      • 第二步:準備工作,建立被增強的類和方法、增強的類和方法
      • 第三步:在spring配置檔案中進行aop相關配置
      • 第四步:在增強類使用注解實作五種通知
        • (1)先在增強類和被增強類上添加注解
        • (2)五種通知的實作
    • (3)xml進行aop細節
      • 1)抽取切入點表達式
      • 2)增強類優先級設定
      • 3)完全注解開發
    • (4)基于XML方式使用AspectJ的aop操作

一、AOP概念

  • 面向切面(/方面)程式設計,利用AOP可以對業務邏輯的各個部分進行隔離,進而使得業務邏輯各部分之間的耦合度降低,提高程式可重用性,提高開發效率。
  • 通俗來講,就是不通過修改源代碼方式,在主幹功能裡面添加新功能。

二、AOP底層原理

(一)動态代理

(1)動态代理有兩種情況

第一種:有接口情況,使用JDK動态代理

  • 建立接口實作類代理對象,增強類的方法
【Spring】AOP詳解 底層原理及操作術語 五種通知類型(使用AspectJ 注解及XML操作)一、AOP概念二、AOP底層原理三、AOP術語四、AOP操作

第二種:沒有接口情況,使用CGLIB動态代理

  • 建立子類的代理對象,增強類的方法
【Spring】AOP詳解 底層原理及操作術語 五種通知類型(使用AspectJ 注解及XML操作)一、AOP概念二、AOP底層原理三、AOP術語四、AOP操作

(2)AOP使用JDK動态代理

  • 在java.lang有類 Proxy,這個類裡面有靜态方法

    newProxyInstance

    ,使用這個方法建立接口實作類代理對象
【Spring】AOP詳解 底層原理及操作術語 五種通知類型(使用AspectJ 注解及XML操作)一、AOP概念二、AOP底層原理三、AOP術語四、AOP操作
【Spring】AOP詳解 底層原理及操作術語 五種通知類型(使用AspectJ 注解及XML操作)一、AOP概念二、AOP底層原理三、AOP術語四、AOP操作
  • 該方法有3個參數:

ClassLoader loader

:類加載器

Class<?>[] interfaces

:增強方法所在的類,這個類實作的接口,支援多個接口

InvocationHandler h

:實作這個接口InvocationHandler,建立代理對象,寫增強的部分

  • 傳回一個指定接口的代理類執行個體,該接口可以将方法調用指派到指定的調用處理程式。
  • 下面是一個jdk動态代理的執行個體:

接口:

package com.springlearn.proxy;

public interface UserDao {
    public void add(int a,int b);
}

           

實作類:

package com.springlearn.proxy;

public class UserDaoImpl implements UserDao {
    @Override
    public void add(int a, int b) {
        int sum=a+b;
        System.out.println("sum:"+sum);
    }
}

           

代理類:

實作了在不改變源代碼的情況下,增強了實作類裡的方法,在方法前後分别進行操作。

package com.springlearn.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class jdbcProxy {
    public static void main(String[] args) {

        UserDao userDaoImpl = new UserDaoImpl();

        //使用jdbc動态代理方式建立UserDao接口實作類代理對象
        //有三個參數:
        //第一個參數:類加載器
        //第二個參數:接口class類型,數組形式
        //第三個參數:InvocationHandler是接口,在接口裡面invoke寫增強部分(匿名内部類)
        Class<?>[] interfaces = {UserDao.class};
        UserDao userDao = (UserDao) Proxy.newProxyInstance(jdbcProxy.class.getClassLoader(), interfaces, new InvocationHandler() {

            //增強的部分
            /*
             * proxy:代理對象
             * method:被增強的方法
             * args:方法裡面參數
             * */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                //方法之前輸出
                System.out.println("方法準備執行.....");

                //讓被增強的方法執行 add執行
                //Method代表類裡面的方法,Method類有invoke,讓類的方法執行(和上面的invoke不同)
                Object result = method.invoke(userDaoImpl, args);

                //方法之後輸出
                System.out.println("方法執行完成了.....");
                return result;
            }
        });

        //調用測試
        userDao.add(1,2);

    }
}

           

輸出結果:

【Spring】AOP詳解 底層原理及操作術語 五種通知類型(使用AspectJ 注解及XML操作)一、AOP概念二、AOP底層原理三、AOP術語四、AOP操作

三、AOP術語

(1)連接配接點

  • 類裡面哪些方法可以被增強,這些方法稱為連接配接點

(2)切入點

  • 實際增強的類裡面的方法,稱為切入點

(3)增強(通知)

  • 實際增強的邏輯部分(新添加功能部分)稱為增強
  • 通知有五種類型

前置通知:在方法之前執行

後置(傳回)通知:在方法之後執行

環繞通知:在方法前後都執行

異常通知:在發生異常時執行

最終(後置)通知:在最後執行,有沒有異常都會執行

(4)切面

  • 切面是具體操作或動作
  • 是把增強作用到方法(切入點)的過程

四、AOP操作

  • Spring架構一般都是基于AspectJ實作AOP操作

(1)AspectJ介紹

(1)spring進行aop操作,一般依賴第三方元件實作 AspectJ;

(2)AspectJ本身是AOP架構,不是spring的組成部分,可以單獨使用,一般經常與spring一起使用,進行aop操作;

(3)spring基于AspectJ實作AOP有兩種方式:

第一種 基于注解方式實作

第二種 基于xml配置檔案方式實作

附加:切入點表達式

(1)切入點表達式作用:知道對哪個類裡面的哪個方法進行增強

(2)文法結構:

*

:代表所有的權限修飾/所有的類/所有的方法

傳回類型

:如果是void可以省略

..

:代表方法中的所有參數

舉例1:對com.atguigu.dao.BookDao類裡面的add進行增強

舉例2:對com.atguigu.dao.BookDao類裡面的所有的方法進行增強

舉例3:對com.atguigu.dao包裡面所有類,類裡面所有方法進行增強

(2)基于注解方式使用AspectJ的aop操作

第一步:導入AspectJ相關的jar包

  • spring-aop-5.2.6.RELEASE.jar

  • AspectJ相關的三個jar包
    【Spring】AOP詳解 底層原理及操作術語 五種通知類型(使用AspectJ 注解及XML操作)一、AOP概念二、AOP底層原理三、AOP術語四、AOP操作

第二步:準備工作,建立被增強的類和方法、增強的類和方法

被增強的類和方法:

package com.springlearn.aop;

/**
 * @program: spring03
 * @description: 被增強的類
 * @author: txg
 * @create: 2021-08-26 11:52
 **/
public class User {
    public void add(){
        System.out.println("add方法執行了……");
    }
}

           

增強的類和方法:

package com.springlearn.aop;

/**
 * @program: spring03
 * @description: 增強的類
 * @author: txg
 * @create: 2021-08-26 11:53
 **/
public class UserProxy {
    //增強的邏輯部分
}

           

第三步:在spring配置檔案中進行aop相關配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <context:component-scan base-package="com.springlearn"></context:component-scan>

    <!--開啟Aspect生成代理對象-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

           

第四步:在增強類使用注解實作五種通知

(1)先在增強類和被增強類上添加注解

增強類:

package com.springlearn.aop;

import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class UserProxy {
}
           

被增強類:

import org.springframework.stereotype.Component;

@Component
public class User {
    public void add(){
        System.out.println("add方法執行了……");
    }
}
           

(2)五種通知的實作

package com.springlearn.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class UserProxy {

    //前置通知
    @Before(value = "execution(* com.springlearn.aop.User.add(..))")
    public void before(){
        System.out.println("前置通知……");
    }

    //後置通知(傳回通知)
    @AfterReturning(value = "execution(* com.springlearn.aop.User.add(..))")
    public void afterReturning() {
        System.out.println("後置通知.............");
    }

    //最終通知
    //總會被執行
    @After(value = "execution(* com.springlearn.aop.User.add(..))")
    public void after() {
        System.out.println("最終通知.............");
    }

    //異常通知
    //被增強的方法出現異常,這個通知執行
    @AfterThrowing(value = "execution(* com.springlearn.aop.User.add(..))")
    public void afterThrowing() {
        System.out.println("異常通知...............");
    }


    //環繞通知
    @Around(value = "execution(* com.springlearn.aop.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        //add方法之前執行
        System.out.println("環繞通知——add方法之前執行……");

        //add方法執行
        proceedingJoinPoint.proceed();

        //add方法之後執行
        System.out.println("環繞通知——add方法之後執行……");
    }

}

           

結果(無異常):

【Spring】AOP詳解 底層原理及操作術語 五種通知類型(使用AspectJ 注解及XML操作)一、AOP概念二、AOP底層原理三、AOP術語四、AOP操作

結果(有異常):

【Spring】AOP詳解 底層原理及操作術語 五種通知類型(使用AspectJ 注解及XML操作)一、AOP概念二、AOP底層原理三、AOP術語四、AOP操作

注意: 這五種通知的執行順序不是絕對不變的,是相對不變的,前置肯定在前面後置肯定在後面,但其他的不一定一直不變。

(3)xml進行aop細節

1)抽取切入點表達式

  • 我們發現,上面的通知注解,每次使用都要寫一遍value的值,也就是包名+方法名;
  • 是以我們可以将這個切入點表達式抽取出來,這樣每次隻引入抽取的這個方法名就可以。
//抽取切入點表達式
    @Pointcut(value = "execution(* com.springlearn.aop.User.add(..))")
    public void pointdemo() {
        
    }
    
    //前置通知
    //使用方法引用就可以
    @Before(value = "pointdemo()")
    public void  before() {
        System.out.println("前置通知............");
    }
           

2)增強類優先級設定

  • 有多個增強類多同一個方法進行增強,設定增強類優先級
  • 在增強類上面添加注解

    @Order(數字類型值)

    ,數字類型值越小優先級越高
@Component
@Aspect
@Order(1)
public class PersonProxy
           

3)完全注解開發

無需使用xml檔案配置,添加一個配置類

【Spring】AOP詳解 底層原理及操作術語 五種通知類型(使用AspectJ 注解及XML操作)一、AOP概念二、AOP底層原理三、AOP術語四、AOP操作
【Spring】AOP詳解 底層原理及操作術語 五種通知類型(使用AspectJ 注解及XML操作)一、AOP概念二、AOP底層原理三、AOP術語四、AOP操作

配置類:

@Configuration
@ComponentScan(basePackages = {"com.springlearn"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class SpringConfig {

}
           

(4)基于XML方式使用AspectJ的aop操作

被增強方法:

package com.springlearn.aop;

public class Book {
    public void buy(){
        System.out.println("bug…………");
    }
}

           

增強方法:

package com.springlearn.aop;

public class BookProxy {

    public void before(){
        System.out.println("前置通知…………");
    }
}

           

xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <context:component-scan base-package="com.springlearn"></context:component-scan>

    <!--開啟Aspect生成代理對象-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

    <bean id="book" class="com.springlearn.aop.Book"></bean>
    <bean id="bookProxy" class="com.springlearn.aop.BookProxy"></bean>

    <!--配置aop增強-->
    <aop:config>
        <aop:pointcut id="p" expression="execution(* com.springlearn.aop.Book.buy(..))"/>

        <!--配置切面-->
        <aop:aspect ref="bookProxy">
            <aop:before method="before" pointcut-ref="p"></aop:before>
        </aop:aspect>
    </aop:config>
    
</beans>
           

aop部分就介紹到這裡啦,你學廢了嘛?

感謝閱讀 ♪(・ω・)ノ