手寫Spring AOP,快來瞧一瞧看一看撒!

狐言不胡言發表於2021-04-21


在這裡插入圖片描述

上面兩篇內容說到了手寫IOC和DI,本篇內容就是手寫AOP,由於內容中使用到了上面兩篇內容寫的程式碼,所以此處貼下連結:

手寫Spring IOC容器:點選進入

手寫Spring DI依賴注入:點選進入

AOP分析

AOP是什麼

Aspect Oriented Programming 面向切面程式設計,在不改變類的程式碼的情況下,對類的方法進行功能增強

那麼如果要實現一個AOP,需要做的事情就是要向使用的使用者提供AOP功能,能夠通過AOP技術實現對類的方法進行功能增強

AOP中的元素

  • Advice 通知,即增強的功能
  • Join points 連線點,可以選擇的方法點,即有哪些可以增強的方法供選擇
  • Pointcut 切入點,選擇切入的方法點,即對哪些方法進行增強
  • Aspact 切面,選擇的多個方法點 + 增強的功能,即Advice和Pointcut的組合
  • Introduction 引入,新增新的方法、屬性到已經存在的類中,叫做引入
  • Weaving 織入,即不改變原來的程式碼,加入增強的功能

手寫AOP的話,上面幾個元素有的是需要使用者提供的:Advice、Pointcut、Aspact,使用者需要提供增強的功能和切入點、切面資訊;AOP框架需要提供Join points、Weaving

AOP提供的功能

  • 需要功能的增強,即Advice通知
  • 對類的方法進行增強,那需要可以選擇的要增強的方法點,即Pointcut切入點
  • 需要在不改變原類程式碼的情況下,進行功能的增強,即Weaving織入

圖解AOP
在這裡插入圖片描述
假設我們現在玩一局王者榮耀,選擇了一個英雄魯班,這個魯班需要參與整個遊戲直到結束,不管是否掛機;剛剛開始的時候,沒有任何裝備,假設魯班身上的各個部位是Join points,即這些部位需要裝備去增強它,那每一個裝備就是Advice增強的功能,比如增加攻速、生命等,現在買了一個鞋子,那麼就是選擇在速度上進行增強,即穿在腳上,那麼就是Pointcut,那麼這個鞋子加上腳就是Aspact切面,上面說的例子可能不夠好,見諒啊!

特點分析

  1. Advice

實現一個Advice,就需要知道Advice有哪些特點,從上面可以知道Advice是使用者來提供的,所以有很多不可控的因素

  • 使用者性:由使用者來提供增強的功能,即增強功能的程式碼是使用者來進行編寫的
  • 多變性:既然使用者來提供,那麼對於不同的需求,增強的邏輯都是不一樣的
  • 可選時機:使用者可以選擇在方法的前面、後面、異常時進行功能增強
  • 多重性:同一個切入點,即同一個方法,可以有多重的增強,不止增強一次
  1. Pointcut

切入點,通過上面知道它也是使用者提供的,所以它的特點和Advice基本上差不多

  • 使用者性:由使用者來指定,對哪些方法進行功能增強
  • 多變性:使用者可以靈活的來指定
  • 多點性:使用者可以選擇在多個方法點上進行功能增強
  1. Weaving

織入,這部分程式碼,是需要框架提供的,即是需要我們自己去實現的邏輯,通過這個可以把增強的功能加入到使用者指定的方法上面

  • 無侵入性:不改變原來類的程式碼,去實現功能的增強
  • 需要我們在AOP框架中自己實現邏輯

通過上面的內容,可以知道,需要做的事情就是Advice、Pointcut、Weaving三個部分,Join points為什麼不需要去實現呢,這部分內容在編寫程式碼的過程中就可以知道了

Advice實現

Advice是由使用者來實現的,這部分邏輯需要使用者寫好然後我們實現AOP的時候來進行使用;我們需要認識使用者的東西,使用者需要使用我們寫的框架,而且還需要隔絕使用者的多變性

那麼需要做的事情是啥呢,可以定義一個標準的介面,使用者通過實現介面來提供不同的增強邏輯,這就是應對變化的方式,面向介面程式設計

定義Advice介面

定義的Advice是一個頂級介面,不需要寫任何的方法,然後根據前置、後置、環繞、異常增強,來去實現Advice介面,那麼這些增強需要的引數是一樣的嗎,請往下看

Advice介面:

/**
 * @className: Advice
 * @description: 通知的標準介面
 * @author TR
 */
public interface Advice {

}

首先,我們知道,增強是對方法進行增強,那麼使用Advice的時候,需要給的就是方法的一些資訊

  1. 前置增強

在方法執行前進行增強,那麼可以知道前置增強是不需要返回值的,需要的引數如下:

  • 方法自身 Method
  • 方法的引數 Object[]
  • 方法所在的類(物件) Object
  1. 後置增強

在方法執行後進行增強,那需要的引數是不是需要增加一個啊,即需要方法的返回值,因為我如果需要對返回值做一下處理,就需要用到它,而且後置增強也是不需要返回值的,需要的引數如下:

  • 方法自身 Method
  • 方法的引數 Object[]
  • 方法所在的類(物件) Object
  • 方法的返回值 Object
  1. 環繞增強

包裹方法進行增強,它是需要包裹整個方法,即方法由它來執行,那麼環繞增強是需要返回值的,這個返回值是需要增加方法的返回值,需要的引數如下:

  • 方法自身 Method
  • 方法的引數 Object[]
  • 方法所在的類(物件) Object
  1. 異常增強

捕獲方法執行時的異常資訊,然後進行增強,而且它也是需要包裹方法進行增強的,即它可以在環繞增強中來實現

通過上面知道,需要定義三個方法:前置增強的方法、後置增強的方法、環繞和異常增強的方法,那這三個方法是定義在一個介面裡面,還是分三個介面呢,根據單一職責原則,還是分三個介面來實現比較好,而且還可以根據不同的介面型別來區分是哪個Advice

定義前置、後置、環繞和異常增強介面

定義前置增強介面MethodBeforeAdvice:

/**
 * @className: MethodBeforeAdvice
 * @description: 前置增強介面
 * @author TR
 */
public interface MethodBeforeAdvice extends Advice {

    /**
     * 前置增強方法
     * @param method: 將要被執行的方法
     * @param target: 執行方法的目標物件
     * @param args: 執行方法的引數
     * @return: void
     **/
    void before(Method method, Object target, Object[] args);
}

定義後置增強介面AfterReturningAdvice:

/**
 * @className: AfterReturningAdvice
 * @description: 後置增強介面
 * @author TR
 */
public interface AfterReturningAdvice extends Advice {
    
    /**
     * 後置增強方法
     * @param method: 將要被執行的方法
     * @param target: 執行方法的目標物件
     * @param args: 執行方法的引數
     * @param returnValue: 執行方法的返回值
     * @return: void
     **/
    void after(Method method, Object target, Object[] args, Object returnValue);
}

定義環繞、異常增強介面MethodInterceptor:

/**
 * @className: MethodInterceptor
 * @description: 環繞、異常增強介面
 * @author TR
 */
public interface MethodInterceptor extends Advice {

    /**
     * 環繞、異常增強方法,在方法實現中需要呼叫目標方法
     * @param method: 將要被執行的方法
     * @param target: 執行方法的目標物件
     * @param args: 執行方法的引數
     * @return: java.lang.Object 執行方法的返回值
     * @throws Throwable
     **/
    Object invoke(Method method, Object target, Object[] args) throws Throwable;
}

類圖如下:
在這裡插入圖片描述

Pointcut實現

Pointcut的特點:

  • 使用者性:由使用者來指定,對哪些方法進行功能增強
  • 多變性:使用者可以靈活的來指定
  • 多點性:使用者可以選擇在多個方法點上進行功能增強

我們需要給使用者提供一個東西,讓使用者可以靈活的來指定方法點,而且我們獲取到的時候又能夠知道,使用者對哪些方法點進行了指定

指定對哪些方法進行增強,指定的資訊是什麼,其實就是一個或者多個方法,而且如果有過載存在呢,所以這個指定的東西,就是一個完整的方法簽名

那該怎麼做到靈活性、多點性呢,這個指定的資訊是可以描述一類方法的,比如:

  • 某個包下某個類的某個方法
  • 某個包下某個類的所有方法
  • 某個包下所有類中以get開頭的方法
  • 某個包下所有類中以get開頭以sevice結尾的方法
  • 某個包下及其子包下所有類中以get開頭以sevice結尾的方法

那麼可以定義一個表示式,來去描述上面的資訊,這個描述的資訊是包名.類名.方法名(引數型別)

每一部分是如何要求的呢?

  • 包名:需要有父子特點,可以模糊匹配
  • 類名:可以模糊匹配
  • 方法名:可以模糊匹配
  • 引數型別:可以有多個

定義的表示式是需要來判斷:是否需要對某個類的某個方法進行增強,那麼需要去匹配類和匹配方法

匹配的表示式:

  • 正規表示式
  • AspactJ表示式

AspactJ表示式學習

/**
 * @className: AspectJTest
 * @description: AspectJ測試類
 */
public class AspectJTest {

    public static void main(String[] args) throws NoSuchMethodException {
        //獲得切點解析器
        PointcutParser pp = PointcutParser
                .getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();
        //切點解析器根據規則,解析出一個型別匹配器
        TypePatternMatcher tp = pp.parseTypePattern("di.*");
        //根據表示式生成一個切點表示式
        PointcutExpression pe = pp.parsePointcutExpression("execution(* demo.beans.BeanFactory.get*(..))");

        //匹配MagicGril的getName方法
        Class<?> cl =  MagicGril.class;
        Method method = cl.getMethod("getName", null);
        //匹配方法執行
        ShadowMatch sm = pe.matchesMethodExecution(method);
        System.out.println("是否匹配到方法:" + sm.alwaysMatches());
        System.out.println(cl.getName() + ",是否匹配表示式:" + pe.couldMatchJoinPointsInType(cl));
        System.out.println(DefaultBeanFactory.class + ",是否匹配表示式:" + pe.couldMatchJoinPointsInType(DefaultBeanFactory.class));

        System.out.println(cl.getName() +"類下的所有方法:");
        for (Method method1 : cl.getMethods()) {
            System.out.println(method1.getName());
        }
    }
}

輸出結果:
在這裡插入圖片描述
通過上面的描述,可以知道,切點該怎麼去設計了

切點需要有的屬性就是切點表示式,需要提供的功能就是匹配類和匹配方法

而且這裡也可以像Advice一樣定義一個頂級介面,因為如果以後有其他的表示式更加好用的話,需要擴充套件,那麼只需要繼承定義的這個頂級介面就行了,不管它內部如何實現,都要去實現我們定義的匹配類和匹配方法的行為

定義PointCut介面

/**
 * @className: PointCut
 * @description: 切點匹配介面
 * @author: TR
 */
public interface PointCut {

    /**
     * 匹配類
     * @author: jinpeng.sun
     * @date: 2021/4/19 13:46
     * @param targetClass: 匹配的目標類
     * @return: boolean
     **/
     boolean matchClass(Class<?> targetClass);

     /**
      * 匹配方法
      * @author: jinpeng.sun
      * @date: 2021/4/19 13:46
      * @param method: 匹配的目標方法
      * @param targetClass: 匹配的目標類
      * @return: boolean
      **/
     boolean matchMethod(Method method, Class<?> targetClass);
}

定義正規表示式的實現類:RegExpressionPointcut

此處不實現,主要使用AspactJ的方式

/**
 * @className: RegExpressionPointcut
 * @description: 正規表示式實現類
 * @author: TR
 */
public class RegExpressionPointcut implements PointCut {

    @Override
    public boolean matchClass(Class<?> targetClass) {
        return false;
    }

    @Override
    public boolean matchMethod(Method method, Class<?> targetClass) {
        return false;
    }
}

定義AspectJ切點表示式的實現類:AspectJExpressionPointcut

/**
 * @className: AspectJExpressionPointcut
 * @description: AspectJ切點表示式實現類
 * @author: TR
 */
public class AspectJExpressionPointcut implements PointCut {

    /** 獲得切點解析器 */
    PointcutParser pp = PointcutParser
            .getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();

    /** 切點表示式的字串形式 */
    private String expression;

    /** AspectJ中的切點表示式 */
    private PointcutExpression pe;

    public AspectJExpressionPointcut(String expression) {
        super();
        this.expression = expression;
        pe = pp.parsePointcutExpression(expression);
    }


    @Override
    public boolean matchClass(Class<?> targetClass) {
        return pe.couldMatchJoinPointsInType(targetClass);
    }

    @Override
    public boolean matchMethod(Method method, Class<?> targetClass) {
        ShadowMatch sm = pe.matchesMethodExecution(method);
        return sm.alwaysMatches();
    }

    public String getExpression() {
        return expression;
    }
}

實現步驟:

  1. maven引入Aspectj的jar
 <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.6</version>
 </dependency>
  1. 獲得切點解析器
 PointcutParser pp = PointcutParser
        .getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();
  1. 解析切點表示式,獲得PointcutExpression
PointcutExpression pe = pp.parsePointcutExpression(
				"execution(* edu.dongnao.courseware.beans.BeanFactory.get*(..))");
  1. 使用PointcutExpression匹配類,不可靠
pe.couldMatchJoinPointsInType(targetClass)
  1. 使用PointcutExpression匹配方法,可靠
ShadowMatch sm = pe.matchesMethodExecution(method);
return sm.alwaysMatches();

類圖如下:
在這裡插入圖片描述

Aspact實現

上面實現了Advice和Pointcut,那麼使用者該如何使用呢?

Advice是使用者提供的功能增強實現,是需要繼承介面的,那麼可以把Advice配置成一個bean,Pointcut是切點表示式,是用來匹配類和方法的,是不需要配置成bean的,只需要提供一個字串形式的表示式就行了,那麼adviceBeanName+expression就組成了切面

下面通過外觀模式來實現切面,把Advice和Pointcut組合起來

定義Advisor介面

/**
 * @className: Advisor
 * @description: 構建切面的介面,組合advice和pointcut
 * @author: TR
 */
public interface Advisor {

    /**
     * 獲取通知bean的名稱
     * @return: java.lang.String
     **/
    String getAdviceBeanName();

    /**
     * 獲取切點表示式
     * @return: java.lang.String
     **/
    String getExpression();
}

定義PointcutAdvisor介面

/**
 * @className: PointcutAdvisor
 * @description: 切點通知者,繼承自Advisor,擴充套件了pointcut
 * @author: TR
 */
public interface PointcutAdvisor extends Advisor {

    /**
     * 獲取切點
     * @return: PointCut
     **/
    PointCut getPointCut();
}

定義AspectJPointcutAdvisor實現類

/**
 * @className: AspectJPointcutAdvisor
 * @description: AspectJ切點表示式的通知者實現類
 * @author: TR
 */
public class AspectJPointcutAdvisor implements PointcutAdvisor {

    /** 通知bean的名稱 */
    private String adviceBeanName;

    /** 表示式 */
    private String expression;

    /** 切點 */
    private PointCut pointCut;

    public AspectJPointcutAdvisor(String adviceBeanName, String expression) {
        this.adviceBeanName = adviceBeanName;
        this.expression = expression;
        this.pointCut = new AspectJExpressionPointcut(expression);
    }

    @Override
    public PointCut getPointCut() {
        return pointCut;
    }

    @Override
    public String getAdviceBeanName() {
        return adviceBeanName;
    }

    @Override
    public String getExpression() {
        return expression;
    }
}

下面的類不予實現,主要使用AspactJ的形式

/**
 * @className: RegPointcutAdvisor
 * @description: 正則切點表示式的通知者實現類
 * @author: TR
 */
public class RegPointcutAdvisor implements PointcutAdvisor {

    /** 通知bean的名稱 */
    private String adviceBeanName;

    /** 表示式 */
    private String expression;

    /** 切點 */
    private PointCut pointCut;

    public RegPointcutAdvisor(String adviceBeanName, String expression) {
        this.adviceBeanName = adviceBeanName;
        this.expression = expression;
        this.pointCut = new RegExpressionPointcut();
    }

    @Override
    public PointCut getPointCut() {
        return null;
    }

    @Override
    public String getAdviceBeanName() {
        return null;
    }

    @Override
    public String getExpression() {
        return null;
    }
}

類圖如下:
在這裡插入圖片描述

從上面的圖中看到了,如果再擴充套件其他的切點表示式,那麼在實現類中,可以看到,有很多的重複程式碼,那麼是不是還可以優化一下呢,可以在實現類的上層再加一個抽象類,來繼承PointcutAdvisor,然後主要的切點實現繼承這個抽象類就行了

Weaving實現

要完成的事情

將使用者提供的增強功能加入到指定的方法上,需要我們來實現

什麼時候做織入

在建立Bean例項的時候,在Bean初始化完成後,再進行織入

怎麼知道Bean要進行增強

需要遍歷Bean類及其所有的方法,然後依次的去匹配使用者指定的切面,如果存在匹配的切面,就是需要增強了

怎麼織入呢,就是需要用到代理了!
在這裡插入圖片描述
使用者需要去註冊切面,我們還需要實現判斷匹配、織入的的邏輯,這部分程式碼改如何寫,需要寫到哪裡呢

現在要做的事情,就是在Bean建立的過程中加一項處理,後面可能會在Bean的建立過程中加入更多的處理,如果這部分程式碼都寫在BeanFactory中,那麼這個類是不是就會有特別多的程式碼,而且後面不方便擴充套件

看下Bean的建立過程:
在這裡插入圖片描述
上面圖中,每個箭頭都是加的擴充套件點,後面可能存在的是,需要在這些點上加入更多的處理邏輯,那麼就需要設計一種方式,在不改變BeanFactory的情況下,能靈活的擴充套件,那麼可以使用觀察者模式,有幾個擴充套件點,就是有幾個主題,六個觀察者
在這裡插入圖片描述

定義觀察者介面BeanPostProcessor

/**
 * @className: BeanPostProcessor
 * @description: 後置處理器,Bean例項化完成後及依賴注入完成後觸發
 * @author: TR
 */
public interface BeanPostProcessor {

    /**
     * bean初始化前的處理
     * @author: TR
     * @param bean: bean例項
     * @param beanName: bean名稱
     * @return: java.lang.Object
     **/
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws Exception {
       return bean;
    }

    /**
     * bean初始化後的處理
     * @author: TR
     * @param bean: bean例項
     * @param beanName: bean名稱
     * @return: java.lang.Object
     **/
    default Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
        return bean;
    }
}

定義通知介面Aware

/**
 * @className: Aware
 * @description: 構建通知介面
 * @date: 2021/4/19 16:38
 * @author: jinpeng.sun
 */
public interface Aware {
}
/**
 * @className: BeanFactoryAware
 * @description: bean工廠構建通知介面
 * @author: TR
 */
public interface BeanFactoryAware extends Aware {

    /**
     * 介面實現者獲得bean工廠方法
     * @author: TR
     * @param bf:
     * @return: void
     **/
    void setBeanFactory(BeanFactory bf);
}

上面的介面主要用來獲得Bean工廠資訊

定義通知者註冊介面AdvisorRegistry

這個介面主要用來把Advisor註冊到增強功能的實現類裡面

/**
 * @className: AdvisorRegistry
 * @description: 通知者註冊介面
 * @author: TR
 */
public interface AdvisorRegistry {

    /**
     * 註冊通知者
     * @author: TR
     * @param advisor:
     * @return: void
     **/
    public void registerAdvisor(Advisor advisor);

    /**
     * 獲得通知者列表
     * @author: TR
     * @date: 2021/4/19 16:45
     *
     * @return: java.util.List<demo.aop.advisor.Advisor>
     **/
    public List<Advisor> getAdvisors();
}

定義增強處理的觀察者實現類AdvisorAutoProxyCreator

/**
 * @className: AdvisorAutoProxyCreator
 * @description: 功能增強的實現類,使用者和框架互動的核心類
 * 使用者通過Advisor提供切面,向DefaultBeanFactory注入該實現
 * 框架內部:DefaultBeanFactory注入ioc容器
 * DefaultBeanFactory呼叫BeanPostProcessor介面相關方法,進行功能增強
 * @author: TR
 */
public class AdvisorAutoProxyCreator implements BeanPostProcessor, BeanFactoryAware, AdvisorRegistry {

    /** 通知者列表 */
    private List<Advisor> advisors;

    /** 當前的bean */
    private BeanFactory beanFactory;

    public AdvisorAutoProxyCreator() {
        this.advisors = new ArrayList<>();
    }

    @Override
    public void registerAdvisor(Advisor advisor) {
        this.advisors.add(advisor);
    }

    @Override
    public List<Advisor> getAdvisors() {
        return this.advisors;
    }

    @Override
    public void setBeanFactory(BeanFactory bf) {
        this.beanFactory = bf;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
        return null;
    }
}

註冊BeanPostProcessor

BeanFactory介面中增加註冊BeanPostProcessor的方法

    /** 註冊BeanPostProcessor */
    void registerBeanPostProcessor(BeanPostProcessor beanPostProcessor);

DefaultBeanFactory實現類中增加如下方法:

    private List<BeanPostProcessor> postProcessors = Collections.synchronizedList(new ArrayList<>());

    @Override
    public void registerBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
        postProcessors.add(beanPostProcessor);
        if (beanPostProcessor instanceof BeanFactoryAware) {
            ((BeanFactoryAware) beanPostProcessor).setBeanFactory(this);
        }
    }

getBean方法中增加bean初始化前後的處理:

 @Override
    public Object getBean(String beanName) throws Exception {
        //獲取bean定義
        BeanDefinition bd = beanDefinitionMap.get(beanName);
        Object bean = doGetBean(beanName);

        //屬性注入
        setPropertyDIValues(bd, bean);

        //bean初始化前的處理
        bean = this.applyPostProcessBeforeInitialization(bean, beanName);

        //bean的生命週期
        if (StringUtils.isNotBlank(bd.getInitMethodName())) {
            doInitMethod(bean,bd);
        }

        //bean初始化後的處理
        bean = this.applyPostProcessAfterInitialization(bean, beanName);

        return bean;
    }
    
    /**
     * bean初始化後的處理
     * @author: TR 
     * @param bean: 
     * @param beanName: 
     * @return: java.lang.Object
     **/
    private Object applyPostProcessAfterInitialization(Object bean, String beanName) throws Exception {
        for (BeanPostProcessor bp : this.postProcessors) {
            bean = bp.postProcessBeforeInitialization(bean, beanName);
        }
        return bean;
    }

    /**
     * bean初始化前的處理
     * @author: TR 
     * @param bean: 
     * @param beanName: 
     * @return: java.lang.Object
     **/
    private Object applyPostProcessBeforeInitialization(Object bean, String beanName) throws Exception {
        for (BeanPostProcessor bp : this.postProcessors) {
            bean = bp.postProcessAfterInitialization(bean, beanName);
        }
        return bean;
    }

下面需要實現的就是判斷bean是否需要增強了,那麼需要獲取到bean的所有方法,然後根據註冊進來的advisors去遍歷它,然後得到切點去匹配類和方法

修改AdvisorAutoProxyCreator類中的postProcessAfterInitialization方法:

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
        //判斷是否需要進行切面增強,及獲得增強的通知實現
        List<Advisor> advisors = getMatchedAdvisors(bean, beanName);
        //如果需要增強,返回增強後的物件
        if (CollectionUtils.isNotEmpty(advisors)) {
            //使用代理模式進行功能增強
        }
        return bean;
    }

下面是獲取匹配的方法程式碼:

    /**
     * 獲取匹配的方法
     * @author: TR
     * @param bean: bean例項
     * @param beanName: bean名稱
     * @return: void
     **/
    private List<Advisor> getMatchedAdvisors(Object bean, String beanName) {
        if (CollectionUtils.isEmpty(advisors)) {
            return null;
        }
        //得到類
        Class<?> beanClass = bean.getClass();
        //得到所有的方法
        List<Method> allMethods = getAllMethodForClass(beanClass);

        //存放匹配的Advisor
        List<Advisor> advisors = new ArrayList<>();
        for (Advisor ad : this.advisors) {
            if (ad instanceof PointcutAdvisor) {
                //判斷是否匹配
                if (isPointcutMatchBean((PointcutAdvisor) ad, beanClass, allMethods)) {
                    advisors.add(ad);
                }
            }
        }
        return advisors;
    }

獲取所有的方法,使用的是Spring framework提供的工具類,來找到類下面所有的方法:

    /**
     * 獲取所有的方法
     * @author: TR
     * @param beanClass:
     * @return: void
     **/
    private List<Method> getAllMethodForClass(Class<?> beanClass) {
        List<Method> allMethods = new LinkedList<>();
        Set<Class<?>> classes = new LinkedHashSet<>(ClassUtils.getAllInterfacesForClassAsSet(beanClass));
        classes.add(beanClass);
        for (Class<?> cc : classes) {
            //根據工具類獲取所有的方法
            Method[] methods = ReflectionUtils.getAllDeclaredMethods(cc);
            for (Method m : methods) {
              allMethods.add(m);
            }
        }
        return  allMethods;
    }

下面是判斷是否有匹配切點規則的方法,使用PointCut中定義的方法來實現:

   /**
     * 判斷是否有匹配切點規則的方法
     * @author: TR
     * @param ad: 切面
     * @param beanClass: 指定的類
     * @param allMethods: 指定的類下面所有的方法
     * @return: void
     **/
    private boolean isPointcutMatchBean(PointcutAdvisor ad, Class<?> beanClass, List<Method> allMethods) {
        PointCut p = ad.getPointCut();

        //匹配類
        if (!p.matchClass(beanClass)) {
            return false;
        }
        for (Method m : allMethods) {
            //匹配方法
            if (p.matchMethod(m, beanClass)) {
                return true;
            }
        }
        return false;
    }

判斷是否增強之後,就是需要進行代理增強了,那麼這裡的實現邏輯又是啥樣的呢
在這裡插入圖片描述
通過上圖可以看到,需要判斷代理的方式,即使用JDK動態代理還是CGLIB動態程式碼,那麼這裡也可以抽象出一個介面。然後不同的代理方式分別去實現

定義AopProxy代理介面

/**
 * @className: AopProxy
 * @description: AOP代理介面,用來建立和獲取代理物件
 * @author: TR
 */
public interface AopProxy {

    Object getProxy();

    Object getProxy(ClassLoader classLoader);
}

JDK動態代理實現

我們知道JDK動態代理是對介面進行的,那麼在實現中需要哪些資料呢?

  • 要實現的介面
  • 目標物件
  • 匹配的Advisors
  • BeanFactory

需要的引數:

  • 需要實現的介面
  • InvocationHandler
/**
 * @className: JdkDynamicAopProxy
 * @description: JDK動態代理實現
 * @author: TR
 */
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {
    private static final Logger logger = LoggerFactory.getLogger(JdkDynamicAopProxy.class);

    //bean名稱
    private String beanName;
    //bean物件,需要被代理的物件
    private Object target;
    //通知列表,需要被增強的功能
    private List<Advisor> advisors;
    //當前的bean
    private BeanFactory beanFactory;

    public JdkDynamicAopProxy(String beanName, Object target, List<Advisor> advisors, BeanFactory beanFactory) {
        this.beanName = beanName;
        this.target = target;
        this.advisors = advisors;
        this.beanFactory = beanFactory;
    }

    @Override
    public Object getProxy() {
        return this.getProxy(target.getClass().getClassLoader());
    }

    @Override
    public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("為" + target + "建立JDK代理。");
        }
        return Proxy.newProxyInstance(classLoader, target.getClass().getInterfaces(), this);
    }

    /**
     * InvocationHandler介面的實現
     * 用來進行代理增強後返回實際的結果
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return AopProxyUtils.applyAdvices(target, method, args, advisors, proxy, beanFactory);
    }
}

CGLIB動態代理實現

CGLIB動態代理可以對介面和類進行,那麼它需要下面的資料:

  • 要繼承的類
  • 要實現的介面
  • 目標物件
  • 匹配的Advisors
  • BeanFactory
  • 構造引數類
  • 構造引數

需要的引數:

  • 繼承的類
  • 需要實現的介面
  • Callback
  • 構造引數型別
  • 構造引數

構造引數型別和構造引數在建立例項的時候會有的

/**
 * @className: CglibDynamicAopProxy
 * @description: Cglib動態代理
 * @author: TR
 */
public class CglibDynamicAopProxy implements AopProxy, MethodInterceptor {
    private static final Logger logger = LoggerFactory.getLogger(CglibDynamicAopProxy.class);
    private static Enhancer enhancer = new Enhancer();

    //bean名稱
    private String beanName;
    //bean物件,需要被代理的物件
    private Object target;
    //通知列表,需要被增強的功能
    private List<Advisor> advisors;
    //當前的bean
    private BeanFactory beanFactory;

    public CglibDynamicAopProxy(String beanName, Object target, List<Advisor> advisors, BeanFactory beanFactory) {
        this.beanName = beanName;
        this.target = target;
        this.advisors = advisors;
        this.beanFactory = beanFactory;
    }

    @Override
    public Object getProxy() {
        return this.getProxy(target.getClass().getClassLoader());
    }

    @Override
    public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("為" + target + "建立cglib代理。");
        }
        Class<?> superClass = this.target.getClass();
        enhancer.setSuperclass(superClass);
        enhancer.setInterfaces(this.getClass().getInterfaces());
        enhancer.setCallback(this);
        Constructor<?> constructor = null;

        try {
            constructor = superClass.getConstructor(new Class<?>[] {});
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        if (constructor != null) {
            return enhancer.create();
        } else {
            BeanDefinition bd = ((DefaultBeanFactory)beanFactory).getBeanDefinition(beanName);
            return enhancer.create(bd.getConstructor().getParameterTypes(), bd.getConstructorArgumentRealValues());
        }
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        return AopProxyUtils.applyAdvices(target, method, objects, advisors, o, beanFactory);
    }
}

AopProxyUtils它是用來幹什麼的呢,從上面可以看到,這部分的處理,主要是用來實現增強的,都是使用Advice來增強的,所以增強的邏輯是一樣的
在這裡插入圖片描述

/**
 * @className: AopProxyUtils
 * @description: aop代理工具類
 * @author: TR
 */
public class AopProxyUtils {


    /**
     * 對方法應用advice增強,獲得最終返回結果
     * @author: TR
     * @param target: 需要被增強的物件
     * @param method: 需要被增強的方法
     * @param args: 增強方法的引數
     * @param advisors: 匹配到的切面
     * @param proxy: bean物件功能增強後的代理物件
     * @param beanFactory: bean工廠
     * @return: java.lang.Object
     **/
    public static Object applyAdvices(Object target, Method method, Object[] args, List<Advisor> advisors,
                                      Object proxy, BeanFactory beanFactory) throws Throwable {
        //獲取對當前方法進行增強的advices
        List<Object> advices = AopProxyUtils.getShouldApplyAdvices(target.getClass(), method, advisors, beanFactory);
        if (CollectionUtils.isEmpty(advices)) {
            return method.invoke(target, args);
        } else {
            //使用責任鏈進行增強
            AopAdviceChainInvocation ac = new AopAdviceChainInvocation(proxy, target, method, args, advices);
            return ac.invoke();
        }
    }

    /**
     * 獲得與方法匹配的Advice切面列表
     * @author: TR
     * @param aClass:
     * @param method:
     * @param advisors:
     * @param beanFactory:
     * @return: java.util.List<demo.aop.advice.Advice>
     **/
    private static List<Object> getShouldApplyAdvices(Class<?> aClass, Method method,
                                                      List<Advisor> advisors, BeanFactory beanFactory) throws Exception {
        if (CollectionUtils.isEmpty(advisors)) {
            return null;
        }
        List<Object> advices = new ArrayList<>();
        for (Advisor advisor : advisors) {
            if (advisor instanceof PointcutAdvisor) {
                if (((PointcutAdvisor)advisor).getPointCut().matchMethod(method, aClass)) {
                    advices.add(beanFactory.getBean(advisor.getAdviceBeanName()));
                }
            }
        }
        return advices;
    }
}

如何來傳遞建立bean例項時獲得的資料到初始化後的Aop中,就是要在BeanDefinition中使用ThreadLocal持有引數值

BeanDefinition增加如下方法:

   /** 獲取構造引數值 */
    public Object[] getConstructorArgumentRealValues();

    /** 設定構造引數值 */
    public void setConstructorArgumentRealValues(Object[] values);

GeneralBeanDefinition類中相應的實現:

   private ThreadLocal<Object[]> realConstructorArgumentValues = new ThreadLocal<>();

    @Override
    public Object[] getConstructorArgumentRealValues() {
        return realConstructorArgumentValues.get();
    }

    @Override
    public void setConstructorArgumentRealValues(Object[] values) {
       this.realConstructorArgumentValues.set(values);
    }

責任鏈AopAdviceChainInvocation類

/**
 * @className: AopAdviceChainInvocation
 * @description: AOP責任鏈呼叫類
 * @author: TR
 */
public class AopAdviceChainInvocation {

    /** AOP責任鏈執行的方法 */
    private static Method invokeMethod;

    static {
        try {
            invokeMethod = AopAdviceChainInvocation.class.getMethod("invoke", null);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    //代理類物件
    private Object proxy;
    //目標類物件
    private Object target;
    //呼叫執行的物件方法
    private Method method;
    //執行方法的引數
    private Object[] args;
    //方法被增強的功能:通知列表
    private List<Object> advices;

    public AopAdviceChainInvocation(Object proxy, Object target, Method method, Object[] args, List<Object> advices) {
          this.proxy = proxy;
          this.target = target;
          this.method = method;
          this.args = args;
          this.advices = advices;
    }

    //責任鏈執行記錄的索引號
    private int i = 0;

    public Object invoke() throws Throwable {
        if (i < this.advices.size()) {
            Object advice = this.advices.get(i++);
            if (advice instanceof MethodBeforeAdvice) {
                //執行前增強
                ((MethodBeforeAdvice)advice).before(method, target, args);
            } else if (advice instanceof MethodInterceptor) {
                //執行環繞增強和異常增強,這裡給的method和物件是invoke方法和鏈物件
                ((MethodInterceptor)advice).invoke(invokeMethod,this, null);
            } else if (advice instanceof AfterReturningAdvice) {
                //後置增強,先獲得結果,在增強
                Object returnValue = this.invoke();
                ((AfterReturningAdvice)advice).after(method, target, args, returnValue);
                return returnValue;
            }
            //回撥
            return this.invoke();
        } else {
            return method.invoke(target, args);
        }
    }
}

定義AopProxyFactory介面

代理的方式實現完了之後,就需要使用它了,這裡使用工廠模式實現:

/**
 * @className: AopProxyFactory
 * @description: AOP代理的工廠介面
 * @author: TR
 */
public interface AopProxyFactory {

    /**
     * 根據引數獲取AOP代理介面的實現
     * @param bean: 例項
     * @param beanName: bean名稱
     * @param advisors: advisors列表
     * @param beanFactory: bean工廠
     * @return: AopProxyFactory
     **/
    AopProxy createAopProxy(Object bean, String beanName, List<Advisor> advisors, BeanFactory beanFactory);

    /**
     * 獲取預設的AopProxyFactory
     * @return: AopProxyFactory
     **/
    static AopProxyFactory getDefaultAopProxyFactory() {
        return new DefaultAopProxyFactory();
    }
}

實現類:

/**
 * @className: DefaultAopProxyFactory
 * @description: 代理工廠實現類
 * @author: TR
 */
public class DefaultAopProxyFactory implements AopProxyFactory {

    @Override
    public AopProxy createAopProxy(Object bean, String beanName, List<Advisor> advisors, BeanFactory beanFactory) {
        //是用JDK動態代理還是CGLIB動態代理
        if (shouldUseJDKDynamicProxy(bean, beanName)) {
            return new JdkDynamicAopProxy(beanName, bean, advisors, beanFactory);
        } else {
            return new CglibDynamicAopProxy(beanName, bean, advisors, beanFactory);
        }
    }

    private boolean shouldUseJDKDynamicProxy(Object bean, String beanName) {
        return false;
    }
}

下面需要修改下AdvisorAutoProxyCreator類中的postProcessAfterInitialization方法:

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
        //判斷是否需要進行切面增強,及獲得增強的通知實現
        List<Advisor> advisors = getMatchedAdvisors(bean, beanName);
        //如果需要增強,返回增強後的物件
        if (CollectionUtils.isNotEmpty(advisors)) {
            //使用代理模式進行功能增強
            bean = this.createProxy(bean, beanName, advisors);
        }
        return bean;
    }
    
    private Object createProxy(Object bean, String beanName, List<Advisor> advisors) {
        return AopProxyFactory.getDefaultAopProxyFactory()
                .createAopProxy(bean, beanName, advisors, beanFactory)
                .getProxy();
    }

DefaultBeanFactory類中快取建構函式的方式需要改變一下

determineConstructor方法中快取的程式碼註釋掉:

if (ct != null) {
//            //對於原型bean,快取起來
//            if (bd.isProtoType()) {
//                bd.setConstructor(ct);
//            }
            return ct;
        } else {
            throw new Exception("找不到對應的構造方法:" + bd);
        }

在createBeanByConstructor中增加程式碼

 /** 通過建構函式構建bean */
    private Object createBeanByConstructor(BeanDefinition bd) throws Exception {
        Object object = null;
        if (CollectionUtils.isEmpty(bd.getConstructorArgumentValues())) {
            //獲得的構造引數值是空的,就不傳參
            object = bd.getBeanClass().newInstance();
        } else {
            //獲得的引數值是空的,就不傳參
            Object[] args = getConstructorArgumentValues(bd);
            if (args == null) {
                object = bd.getBeanClass().newInstance();
            } else {
                // 根據引數匹配決定構造方法,然後進行例項化呼叫
                bd.setConstructorArgumentRealValues(args);
                Constructor<?> ct = this.determineConstructor(bd, args);
                // 快取建構函式由determineConstructor 中移到了這裡,無論原型否都快取,因為後面AOP需要用
                bd.setConstructor(ct);
                return ct.newInstance(args);
            }
        }
        return object;
    }

測試一下

前置增強實現:

public class MyBeforeAdvice implements MethodBeforeAdvice {

    @Override
    public void before(Method method, Object target, Object[] args) {
        System.out.println(this + " 對 " + target + " 進行了前置增強!");
    }
}

後置增強實現:

public class MyAfterReturningAdvice implements AfterReturningAdvice {

    @Override
    public void after(Method method, Object target, Object[] args, Object returnValue) {
        System.out.println(this + " 對 " + target + " 做了後置增強,得到的返回值=" + returnValue);
    }
}

環繞增強實現:

public class MyMethodInterceptor implements MethodInterceptor {

    @Override
    public Object invoke(Method method, Object target, Object[] args) throws Throwable {
        System.out.println(this + "對 " + target + "進行了環繞--前增強");
        Object ret = method.invoke(target, args);
        System.out.println(this + "對 " + target + "進行了環繞 --後增強。方法的返回值為:" + ret);
        return ret;
    }
}

測試類:

public class AopTest {
    static PreBuildBeanFactory bf = new PreBuildBeanFactory();

    @Test
    public void testCirculationDI() throws Throwable {

        GeneralBeanDefinition bd = new GeneralBeanDefinition();
        bd.setBeanClass(Lad.class);
        List<Object> args = new ArrayList<>();
        args.add("孫悟空");
        args.add(new BeanReference("baigujing"));
        bd.setConstructorArgumentValues(args);
        bf.registerBeanDefinition("swk", bd);

        bd = new GeneralBeanDefinition();
        bd.setBeanClass(MagicGirl.class);
        args = new ArrayList<>();
        args.add("白骨精");
        bd.setConstructorArgumentValues(args);
        bf.registerBeanDefinition("baigujing", bd);

        bd = new GeneralBeanDefinition();
        bd.setBeanClass(Renminbi.class);
        bf.registerBeanDefinition("renminbi", bd);

        // 前置增強advice bean註冊
        bd = new GeneralBeanDefinition();
        bd.setBeanClass(MyBeforeAdvice.class);
        bf.registerBeanDefinition("myBeforeAdvice", bd);

        // 環繞增強advice bean註冊
        bd = new GeneralBeanDefinition();
        bd.setBeanClass(MyMethodInterceptor.class);
        bf.registerBeanDefinition("myMethodInterceptor", bd);

        // 後置增強advice bean註冊
        bd = new GeneralBeanDefinition();
        bd.setBeanClass(MyAfterReturningAdvice.class);
        bf.registerBeanDefinition("myAfterReturningAdvice", bd);

        // 往BeanFactory中註冊AOP的BeanPostProcessor
        AdvisorAutoProxyCreator aapc = new AdvisorAutoProxyCreator();
        bf.registerBeanPostProcessor(aapc);
        // 向AdvisorAutoProxyCreator註冊Advisor
        aapc.registerAdvisor(
                new AspectJPointcutAdvisor("myBeforeAdvice", "execution(* demo.di.MagicGirl.*(..))"));
        // 向AdvisorAutoProxyCreator註冊Advisor
        aapc.registerAdvisor(
                new AspectJPointcutAdvisor("myMethodInterceptor", "execution(* demo.di.Lad.say*(..))"));
        // 向AdvisorAutoProxyCreator註冊Advisor
        aapc.registerAdvisor(new AspectJPointcutAdvisor("myAfterReturningAdvice",
                "execution(* demo.di.Renminbi.*(..))"));

        bf.preInstantiateSingletons();

        System.out.println("-----------------myBeforeAdvice---------------");
        MagicGirl gril = (MagicGirl) bf.getBean("baigujing");
        gril.getFriend();
        gril.getName();

        System.out.println("----------------myMethodInterceptor----------------");
        Boy boy = (Boy) bf.getBean("swk");
        boy.sayLove();

        System.out.println("-----------------myAfterReturningAdvice---------------");
        Renminbi rmb = (Renminbi) bf.getBean("renminbi");
        rmb.pay();
    }
}

執行結果:
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
本文其他知識點:

相關文章