
上面兩篇內容說到了手寫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();
}
}
運行結果:
本文其他知識點: