上面兩篇內容說到了手寫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切面,上面說的例子可能不夠好,見諒啊!
特點分析
- Advice
實現一個Advice,就需要知道Advice有哪些特點,從上面可以知道Advice是使用者來提供的,所以有很多不可控的因素
- 使用者性:由使用者來提供增強的功能,即增強功能的程式碼是使用者來進行編寫的
- 多變性:既然使用者來提供,那麼對於不同的需求,增強的邏輯都是不一樣的
- 可選時機:使用者可以選擇在方法的前面、後面、異常時進行功能增強
- 多重性:同一個切入點,即同一個方法,可以有多重的增強,不止增強一次
- Pointcut
切入點,通過上面知道它也是使用者提供的,所以它的特點和Advice基本上差不多
- 使用者性:由使用者來指定,對哪些方法進行功能增強
- 多變性:使用者可以靈活的來指定
- 多點性:使用者可以選擇在多個方法點上進行功能增強
- Weaving
織入,這部分程式碼,是需要框架提供的,即是需要我們自己去實現的邏輯,通過這個可以把增強的功能加入到使用者指定的方法上面
- 無侵入性:不改變原來類的程式碼,去實現功能的增強
- 需要我們在AOP框架中自己實現邏輯
通過上面的內容,可以知道,需要做的事情就是Advice、Pointcut、Weaving三個部分,Join points為什麼不需要去實現呢,這部分內容在編寫程式碼的過程中就可以知道了
Advice實現
Advice是由使用者來實現的,這部分邏輯需要使用者寫好然後我們實現AOP的時候來進行使用;我們需要認識使用者的東西,使用者需要使用我們寫的框架,而且還需要隔絕使用者的多變性
那麼需要做的事情是啥呢,可以定義一個標準的介面,使用者通過實現介面來提供不同的增強邏輯,這就是應對變化的方式,面向介面程式設計
定義Advice介面
定義的Advice是一個頂級介面,不需要寫任何的方法,然後根據前置、後置、環繞、異常增強,來去實現Advice介面,那麼這些增強需要的引數是一樣的嗎,請往下看
Advice介面:
/**
* @className: Advice
* @description: 通知的標準介面
* @author TR
*/
public interface Advice {
}
首先,我們知道,增強是對方法進行增強,那麼使用Advice的時候,需要給的就是方法的一些資訊
- 前置增強
在方法執行前進行增強,那麼可以知道前置增強是不需要返回值的,需要的引數如下:
- 方法自身 Method
- 方法的引數 Object[]
- 方法所在的類(物件) Object
- 後置增強
在方法執行後進行增強,那需要的引數是不是需要增加一個啊,即需要方法的返回值,因為我如果需要對返回值做一下處理,就需要用到它,而且後置增強也是不需要返回值的,需要的引數如下:
- 方法自身 Method
- 方法的引數 Object[]
- 方法所在的類(物件) Object
- 方法的返回值 Object
- 環繞增強
包裹方法進行增強,它是需要包裹整個方法,即方法由它來執行,那麼環繞增強是需要返回值的,這個返回值是需要增強方法的返回值,需要的引數如下:
- 方法自身 Method
- 方法的引數 Object[]
- 方法所在的類(物件) Object
- 異常增強
捕獲方法執行時的異常資訊,然後進行增強,而且它也是需要包裹方法進行增強的,即它可以在環繞增強中來實現
通過上面知道,需要定義三個方法:前置增強的方法、後置增強的方法、環繞和異常增強的方法,那這三個方法是定義在一個介面裡面,還是分三個介面呢,根據單一職責原則,還是分三個介面來實現比較好,而且還可以根據不同的介面型別來區分是哪個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;
}
}
實現步驟:
- maven引入Aspectj的jar
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
- 獲得切點解析器
PointcutParser pp = PointcutParser
.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();
- 解析切點表示式,獲得PointcutExpression
PointcutExpression pe = pp.parsePointcutExpression(
"execution(* edu.dongnao.courseware.beans.BeanFactory.get*(..))");
- 使用PointcutExpression匹配類,不可靠
pe.couldMatchJoinPointsInType(targetClass)
- 使用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();
}
}
執行結果:
本文其他知識點: