一、AOP分析
問題1:AOP是什么?
Aspect Oriented Programming 面向切面編程,在不改變類的代碼的情況下,對類方法進行功能增強。
問題2:我們需要做什么?
在我們的框架中要向使用用戶提供AOP功能,讓他們可以通過AOP技術實現對類方法進行功能增強。
從"Aspect Oriented Programming 面向切面編程,在不改變類的代碼的情況下,對類方法進行功能增強"這句話我們能得到下面的這些信息:
二、AOP概念學習
我們先來看一下下面的這張圖
說明:
程序運行時會調用很多方法,調用的很多方法就叫做Join points(連接點,可以被選擇來進行增強的方法點),在方法的前或者后選擇一個地方來切入,切入的的地方就叫做Pointcut(切入點,選擇增強的方法),然后把要增強的功能(Advice)加入到切入點所在的位置。Advice和Pointcut組成一個切面(Aspect)
AOP的幾個概念:
Advice、Pointcut、Weaving的特點:
Advice(功能增強):
1)用戶性:由用戶提供增強功能的邏輯代碼
2)變化的:不同的增強需求,會有不同的邏輯
3)可選時機:可選擇在方法前、后、異常時進行功能增強
4)多重的:同一個切入點上可以有多重增強
Pointcut(切入點):
1)用戶性:由用戶來指定
2)變化的:用戶可靈活指定
3)多點性:用戶可以選擇在多個點上進行功能增強
Weaving(織入):
1)無侵入性,因為不改變原類的代碼
2)我們在框架中實現
三、切面實現
我們將要分析Advice、Pointcut、Aspect這三個東西
1. Advice設計
Advice是由用戶來提供,我們來使用,它是多變得。
問題1:我們如何能識別用戶提供的東西?用戶在我們寫好框架以后使用我們的框架。
問題2:如何讓我們的代碼隔絕用戶提供的多變?
解決方法:
我們定義一套標准接口,用戶通過實現接口來提供它們不同的邏輯。
為了應對變化,這里使用到了設計原則:面向接口編程
Advice的特點:可選時機,可選擇在方法前、后、異常時進行功能增強
1)有的Advice是在方法執行前進行增強——前置增強
2)有的Advice是在方法執行后進行增強——后置增強
3)有的Advice會在方執行前后都進行增強——環繞增強
4)有的Advice則只是在方法執行拋出異常時進行增強——異常處理增強
問題1:我們需要做什么?
定義標准接口方法,讓用戶可以實現它,提供各種增強。
問題2:這四種增強所需的參數一樣嗎?
下面我們來一個一個的分析
1.1 前置增強分析
前置增強:在方法執行前進行增強
問題1:它可能需要什么參數?
目的是對方法進行增強,應該需要的是方法相關的信息。
問題2:運行時,方法有哪些信息?
方法本身 Method
方法屬於哪個類 Object
方法的參數 Object [ ]
方法的返回值
...........
問題3:前置增強可能需要什么參數?
方法本身 Method
方法屬於哪個類 Object
方法的參數 Object [ ]
問題3:前置增強的返回值是什么?
在方法執行前進行增強,不需要返回值
1.2 后置增強分析
后置增強:在方法執行后進行增強
問題1:后置增強可能需要什么參數?
方法本身 Method
方法屬於哪個類 Object
方法的參數 Object [ ]
方法的返回值
問題2:后置增強的返回值是什么?
在方法執行后進行增強,不需要返回值
1.3 環繞增強分析
環繞增強:方法執行前后進行增強(包裹方法進行增強)
問題1:它可能需要什么參數?
方法本身 Method
方法屬於哪個類 Object
方法的參數 Object [ ]
問題2:環繞增強的返回值是什么?
方法被它包裹,也即方法將由它來執行,它需要返回方法的返回值。Object
1.4 異常處理增強分析
異常處理增強:捕獲方法執行時的異常,進行增強處理。
問題1:它可能需要什么參數?
異常信息
問題2:進行異常處理增強需要包裹方法嗎?
需要,把執行代碼用try包起來,捕獲到哪個異常就在哪里進行增強
問題3:那它可否在環繞中實現?
可以
1.5 經過前面的分析,我們一共需要定義三個方法,因為1.3和1.4可以都用環繞實現
問題1:是把這三個方法定義在一個接口中,還是分別定義在三個接口中?
分三個接口,這樣可以通過類型來區分不同的增強(Advice)
類圖如下:
對應的代碼實現:
前置增強:
package com.study.spring.aop.advice; import java.lang.reflect.Method; /** * * @Description: 前置增強接口 * @author leeSmall * @date 2018年12月1日 * */ public interface MethodBeforeAdvice extends Advice { /** * 實現該方法進行前置增強 * * @param method 被增強的方法 * * @param args 被增強的方法的參數 * * @param target 被增強的目標對象(被增強的方法所在的類) * * @throws Throwable 異常 */ void before(Method method, Object[] args, Object target) throws Throwable; }
后置增強:
package com.study.spring.aop.advice; import java.lang.reflect.Method; /** * * @Description: 后置增強接口 * @author leeSmall * @date 2018年12月1日 * */ public interface AfterReturningAdvice extends Advice { /** * 實現該方法,提供后置增強 * * @param returnValue 被增強的方法的返回值 * * @param method 被增強的方法 * * @param args 被增強的方法的參數 * * @param target 被增強的目標對象(被增強的方法所在的類) * * @throws Throwable 異常 */ void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable; }
方法進行環繞(前置、后置)增強、異常處理增強:
package com.study.spring.aop.advice; import java.lang.reflect.Method; /** * * @Description: 對方法進行環繞(前置、后置)增強、異常處理增強接口 * @author leeSmall * @date 2018年12月1日 * */ public interface MethodInterceptor extends Advice { /** * 對方法進行環繞(前置、后置)增強、異常處理增強,方法實現中需調用目標方法。 * * @param method 被增強的方法 * * @param args 被增強的方法的參數 * * @param target 被增強的目標對象(被增強的方法所在的類) * * @return Object 被增強的方法的返回值 * @throws Throwable */ Object invoke(Method method, Object[] args, Object target) throws Throwable; }
2. Pointcut設計
2.1 Pointcut分析
Pointcut的特點:
1)用戶性:由用戶來指定
2)變化的:用戶可靈活指定
3)多點性:用戶可以選擇在多個點上進行增強
我們需要做什么?
為用戶提供一個東西,讓他們可以靈活的指定多個方法點,而且我們又能懂
切入點是由用戶來指定在哪些方法點上進行增強,那么這個哪些方法如何來表示,能滿足上面的點?
分析:
1)指定哪些方法,是不是一個描述信息
2)如何來指定一個方法——某類的某個方法
3)方法重載怎么辦——加上參數類型
4)有沒有感覺其實就是一個完整的方法簽名
com.study.design.mode.samples.proxy.Girl.dating(float length)
com.study.design.mode.samples.proxy.Girl.dating(long time)
5)如何做到多點性,靈活性?在一個描述中指定一類類的某些方法?
a)某個包下的某個類的某個方法
b)某個包下的所有類中的所有方法
c)某個包下的所有類中的do開頭的方法
d)某個包下的以service結尾的類中的do開頭的方法
e)某個包下的及其子包下的以service結尾的類中的do開頭的方法
總結:我們需要一個能描述上面a-e這些信息的表達式
6)要表達哪些信息?
包名、類名、方法名(參數類型)
7)每部分的要求是怎樣的?
包名:有父子特點,要能模糊匹配
類名:要能模糊匹配
方法:要能模糊匹配
參數類型:參數可以有多個
8)這個表達式將被我們用來決定是否需要對某個類的某個方法進行功能增強,這個決定過程應該是怎樣的?
匹配類、匹配方法
9)一個表達式不好實現,分成多個表達式進行組合是否更容易些?
可以這么考慮
10)我們掌握的表達式有哪些?
正則表達式
Ant Path表達式
AspectJ的Pointcut表達式——execution(* com.study.design.mode.samples.proxy.Girl.*(..))
總結:正則表達式是可以的,AspectJ本就是切面編程的組件,也是可以的
2.2 AspectJ的Pointcut表達式語法學習
AspectJ是什么?
AspectJ是java里面切面編程的庫,能夠幫助我們完成預編譯時的代碼增強,和eclispe配合使用可以生成相關的字節碼。我們在AOP里面只使用了他的表達式解析匹配相關的API
AspectJ的Pointcut表達式是用來表示應該在哪個類的哪個方法進行切入進行方法增強的。
語法如下:
示例:
* com.study.design.mode.samples.proxy.Girl.*(..)
2.3 Pointcut設計
下面開始對Pointcut進行接口、類設計了
問題1:切點應有什么屬性?
切點定義表達式
問題2:切點應對外提供什么行為(方法)?
問題3:切點將被我們用來做什么?
對類、方法進行匹配
切點應該提供匹配類、匹配方法的行為
問題4:如果在我們設計的框架中要能靈活擴展的切點的實現方式,我們該如何設計?
這又是一個支持可多變的問題,像通知一樣,我們來定義一套標准接口,定義好基本行為,面向接口編程,屏蔽掉具體的實現
無論哪種實現,都實現匹配類、匹配方法的接口
Pointcut標准接口的類圖:
Pointcut代碼
package com.study.spring.aop.pointcut; import java.lang.reflect.Method; /** * * @Description: Pointcut標准接口 * @author leeSmall * @date 2018年12月2日 * */ public interface Pointcut { //匹配類 boolean matchsClass(Class<?> targetClass); //匹配方法 boolean matchsMethod(Method method, Class<?> targetClass); }
下面我們來實現AspectJ表達式的Pointcut
AspectJExpressionPointcut的實現
可以使用AspectJ的API把這兩個match方法實現
實現步驟:
1)引入AspectJ的jar
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.1</version> </dependency>
2)掌握AspectJ的API的使用,我們只使用它的切點表達式解析匹配部分
a)入口:org.aspectj.weaver.tools.PointcutParser 獲得切點解析器
PointcutParser pp = PointcutParser
.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();
b)解析表達式得到org.aspectj.weaver.tools.PointcutExpression
PointcutExpression pe = pp.parsePointcutExpression("execution(* com.study.spring.samples.*.set*(..))");
c)用PointcutExpression匹配類,匹配有時候不准,后面可以通過匹配方法來精確匹配
pe.couldMatchJoinPointsInType(ABean.class)
d)用PointcutExpression匹配方法,可以實現精確匹配
//拿到匹配類的Class Class<?> cl = ABean.class; //通過方法名拿到Method Method aMethod = cl.getMethod("doSomthing", null); //使用PointcutExpression匹配方法拿到匹配對象ShadowMatch ShadowMatch sm = pe.matchesMethodExecution(aMethod); //從匹配對象ShadowMatch里面查看是否匹配 System.out.println(sm.alwaysMatches());
AspectJExpressionPointcut的具體代碼:
package com.study.spring.aop.pointcut; import java.lang.reflect.Method; import org.aspectj.weaver.tools.PointcutExpression; import org.aspectj.weaver.tools.PointcutParser; import org.aspectj.weaver.tools.ShadowMatch; /** * * @Description: AspectJ表達式的Pointcut * @author leeSmall * @date 2018年12月2日 * */ public class AspectJExpressionPointcut implements Pointcut { //獲得切點解析器 private static PointcutParser pp = PointcutParser .getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution(); //表達式 private String expression; //Pointcut表達式對象 private PointcutExpression pe; public AspectJExpressionPointcut(String expression) { super(); this.expression = expression; //解析表達式得到org.aspectj.weaver.tools.PointcutExpression pe = pp.parsePointcutExpression(expression); } //匹配類 用PointcutExpression匹配類,匹配有時候不准,后面可以通過匹配方法來精確匹配 @Override public boolean matchsClass(Class<?> targetClass) { return pe.couldMatchJoinPointsInType(targetClass); } //匹配方法 用PointcutExpression匹配方法,可以實現精確匹配 @Override public boolean matchsMethod(Method method, Class<?> targetClass) { ShadowMatch sm = pe.matchesMethodExecution(method); return sm.alwaysMatches(); } public String getExpression() { return expression; } }
到這里,功能增強(Advice)和Pointcut我們都實現了,下面來看看用戶如何使用我們提供的東西了
說明:
從上面的Advice和Pointcut的類圖我們可以知道,用戶要使用我們提供的Advice和Pointcut,只需要實現自己的一個Advice,如MyBeforeAdvice,並把實現的Advice配置成bean,然后傳入一個表達式到AspectJExpressionPointcut里面就可以了。
配置的實現的Advice的bean的名字(adviceBeanName)和表達式(expression)組成一個切面
上面的使用還是不太好,這個時候我們需要為上面的使用抽象出一個接口,使得用戶的使用更加簡單!!!請繼續看下面的內容
2.4 Aspect(切面)設計
為用戶提供更簡單的外觀,Advisor(通知者)組合Advice和Pointcut
Advisor(通知者)代碼實現:
package com.study.spring.aop.advisor; /** * * @Description: Advisor(通知者)接口 * @author leeSmall * @date 2018年12月2日 * */ public interface Advisor { String getAdviceBeanName(); String getExpression(); }
基於切入點的通知者實現代碼:
package com.study.spring.aop.advisor; import com.dn.spring.aop.pointcut.Pointcut; /** * * @Description:基於切入點的通知者實現 * @author leeSmall * @date 2018年12月2日 * */ public interface PointcutAdvisor extends Advisor { Pointcut getPointcut(); }
AspectJPointcutAdvisor(基於AspectJ切入點的通知者實現)代碼實現:
package com.study.spring.aop.advisor; import com.dn.spring.aop.pointcut.AspectJExpressionPointcut; import com.dn.spring.aop.pointcut.Pointcut; /** * * @Description: 基於AspectJ切入點的通知者實現 用戶配的一個切面,包含Advice(功能增強)和Pointcut(切入點) * @author leeSmall * @date 2018年12月2日 * */ public class AspectJPointcutAdvisor implements PointcutAdvisor { //用戶配置的advice的bean的名字 private String adviceBeanName; //切入點表達式 private String expression; //AspectJ表達式切入點對象 private AspectJExpressionPointcut pointcut; public AspectJPointcutAdvisor(String adviceBeanName, String expression) { super(); this.adviceBeanName = adviceBeanName; this.expression = expression; this.pointcut = new AspectJExpressionPointcut(this.expression); } @Override public Pointcut getPointcut() { return this.pointcut; } @Override public String getAdviceBeanName() { return this.adviceBeanName; } @Override public String getExpression() { return this.expression; } }
擴展不同的Advisor實現:
還可把AspectJPointcutAdvisor和RegExpressionPointcutAdvisor的公共部分提取出來減少冗余代碼:
四、織入實現
1. Weaving織入分析
織入要完成什么?
將用戶提供的增強功能加到指定的方法上。這一部分是我們要實現的
思考以下問題:
問題1:在什么時候做織入?
創建bean實例的時候,在bean初始化完成后,再對其進行增強
問題2:如何確定bean要增強?
對bean類及其方法挨個匹配用戶指定的切面,如果有切面匹配就是要增強的
問題3:如何織入
代理
2. Weaving織入設計
整理一下AOP的使用流程,幫助我們更好地去設計織入
問題1:用戶到哪里去注冊切面?
BeanFactory?
問題2:判斷匹配、織入的邏輯寫在哪里?
寫在BeanFactory中?
我們現在是不是要在Bean創建的過程中加入一項處理?后續可能在Bean創建過程中還會加入更多別的處理,如果直接在BeanFactory中實現會有什么不好?
BeanFactory的代碼會越來越多
不易擴展
那么該怎么來設計呢?
回顧一下Bean產生的過程中都經歷了什么
在Bean產生的過程中,會有很多的處理邏輯加入到過程的不同階段,比如bean初始化前、bean初始化后等等
我們如何來設計能讓我們的BeanFactory一次寫好后,后面就不改代碼,就可以靈活擴展呢?
在各個節點加入擴展點、加入注冊機制
什么是擴展點,什么是注冊機制?
這里就需要用到前面學習的觀察者模式(監聽模式)了,BeanFactory就是主題(保證寫好一次后就不在改變),6個擴展點就是觀察者,主題面向觀察者編程,BeanFactory(主題)里面可以添加、刪除、通知6個擴展點(觀察者)
觀察者模式類圖:
說明:
主題Subject面向觀察者接口Observer編程,主題里面可以添加、刪除和通知觀察者Observer;
注意每個觀察者都有一個回調方法update,如果有變化就會在主題的notifyObservers()方法里面調用update方法,把最新的變化給到觀察者
變化之處:觀察者會變,觀察者的數量會變。
不變:主題的代碼要不受觀察者變化的影響。
觀察者模式定義:
定義了對象之間一對多的依賴關系,當一端對象改變狀態時,它的所有依賴者都會收到通知並自動更新(被調用更新方法)。也稱為:監聽模式、發布訂閱模式。提供一種對象之間松耦合的設計方式。
使用觀察者模式來加入我們的AOP織入
代碼實現:
AOP增強處理接口:
package com.study.spring.beans; /** * * @Description: AOP增強處理接口 * @author leeSamll * @date 2018年12月2日 * */ public interface BeanPostProcessor { //bean初始化前增強 default Object postProcessBeforeInitialization(Object bean, String beanName) throws Throwable { return bean; } //bean初始化后增強 default Object postProcessAfterInitialization(Object bean, String beanName) throws Throwable { return bean; } }
Advisor注冊接口:
package com.study.spring.aop.advisor; import java.util.List; /** * * @Description: Advisor注冊接口 * @author leeSamll * @date 2018年12月2日 * */ public interface AdvisorRegistry { //注冊Advisor public void registAdvisor(Advisor ad); //獲取Advisor public List<Advisor> getAdvisors(); }
AOP增強處理的觀察者實現:
package com.study.spring.aop; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.apache.commons.collections4.CollectionUtils; import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; import com.dn.spring.aop.advisor.Advisor; import com.dn.spring.aop.advisor.AdvisorRegistry; import com.dn.spring.aop.advisor.PointcutAdvisor; import com.dn.spring.aop.pointcut.Pointcut; import com.dn.spring.beans.BeanFactory; import com.dn.spring.beans.BeanFactoryAware; import com.dn.spring.beans.BeanPostProcessor; /** * * @Description: AOP增強處理的觀察者實現 * @author leeSamll * @date 2018年12月2日 * */ public class AdvisorAutoProxyCreator implements AdvisorRegistry, BeanPostProcessor, BeanFactoryAware { private List<Advisor> advisors; private BeanFactory beanFactory; public AdvisorAutoProxyCreator() { this.advisors = new ArrayList<>(); } public void registAdvisor(Advisor ad) { this.advisors.add(ad); } public List<Advisor> getAdvisors() { return advisors; } @Override public void setBeanFactory(BeanFactory bf) { this.beanFactory = bf; } //后置增強 public Object postProcessAfterInitialization(Object bean, String beanName) throws Throwable { // 在此判斷bean是否需要進行切面增強 List<Advisor> matchAdvisors = getMatchedAdvisors(bean, beanName); // 如需要就進行增強,再返回增強的對象。 if (CollectionUtils.isNotEmpty(matchAdvisors)) { bean = this.createProxy(bean, beanName, matchAdvisors); } return bean; } //在此判斷bean是否需要進行切面增強 private List<Advisor> getMatchedAdvisors(Object bean, String beanName) { if (CollectionUtils.isEmpty(advisors)) { return null; } // 得到類、類的所有方法 Class<?> beanClass = bean.getClass(); List<Method> allMethods = this.getAllMethodForClass(beanClass); // 存放匹配的Advisor的list List<Advisor> matchAdvisors = new ArrayList<>(); // 遍歷Advisor來找匹配的 for (Advisor ad : this.advisors) { if (ad instanceof PointcutAdvisor) { if (isPointcutMatchBean((PointcutAdvisor) ad, beanClass, allMethods)) { matchAdvisors.add(ad); } } } return matchAdvisors; } //獲取類的所有方法,包括繼承的父類和實現的接口里面的方法 private List<Method> getAllMethodForClass(Class<?> beanClass) { List<Method> allMethods = new LinkedList<>(); //獲取beanClass的所有接口 Set<Class<?>> classes = new LinkedHashSet<>(ClassUtils.getAllInterfacesForClassAsSet(beanClass)); classes.add(beanClass); //遍歷所有的類和接口反射獲取到所有的方法 for (Class<?> clazz : classes) { Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz); for (Method m : methods) { allMethods.add(m); } } return allMethods; } //判斷類及類的方法是否和切面匹配 private boolean isPointcutMatchBean(PointcutAdvisor pa, Class<?> beanClass, List<Method> methods) { Pointcut p = pa.getPointcut(); // 首先判斷類是否匹配 if (!p.matchsClass(beanClass)) { return false; } // 再判斷是否有方法匹配 for (Method method : methods) { if (p.matchsMethod(method, beanClass)) { return true; } } return false; } //創建代理對象增強 private Object createProxy(Object bean, String beanName, List<Advisor> matchAdvisors) throws Throwable { // 通過AopProxyFactory工廠去完成選擇、和創建代理對象的工作。 return AopProxyFactory.getDefaultAopProxyFactory().createAopProxy(bean, beanName, matchAdvisors, beanFactory) .getProxy(); } }
Advisor(通知者)接口:
package com.study.spring.aop.advisor; /** * * @Description: Advisor(通知者)接口 * @author leeSmall * @date 2018年12月2日 * */ public interface Advisor { //獲取配置的Advice的bean的名字 String getAdviceBeanName(); //獲取切入點表達式 String getExpression(); }
在IOC容器(bean工廠)接口里面增加注冊AOP織入(注冊AOP增強處理的觀察者實現)的方法:
package com.study.spring.beans; /** * * @Description: IOC容器(bean工廠)接口:負責創建bean實例 * @author leeSmall * @date 2018年11月29日 * */ public interface BeanFactory { /** * 獲取bean * * @param name bean的名字 * * @return bean 實例 * @throws Exception */ Object getBean(String name) throws Throwable; //注冊AOP織入(注冊AOP增強處理的觀察者實現) void registerBeanPostProcessor(BeanPostProcessor bpp); }
3. Weaving織入實現
3.1 判斷bean是否需要織入增強
問題1:如何判斷bean實例是否要增強?
1)通過反射獲取bean類及所有方法
java.lang.Class.getMethods() : Method[ ] 獲取所有修飾符為public的方法,包括實現的接口和繼承的父類里面的所有public修飾的方法
java.lang.Class.getMethod(String, Class<?>...) : Method 獲取一個指定的public修飾符修飾的方法,包括實現的接口里面的public修飾的方法
java.lang.Class.getDeclaredMethods() : Method[ ] 獲取類里面的所有方法,包括public, protected, default (package) access, and private 這些修飾符修飾的方法,但是不能獲取從父類繼承來的方法
總結:上面的三種方式都不能保證獲取到所有的方法,如果要獲取所有的方法就得遞歸調用,找到所有的類再去獲取對應的方法,這一點Spring已經有實現好了的了,我們直接拿來用就行了。
Spring獲取類的所有方法的API如下:
//獲取類的所有方法,包括繼承的父類和實現的接口里面的方法 private List<Method> getAllMethodForClass(Class<?> beanClass) { List<Method> allMethods = new LinkedList<>(); //獲取beanClass的所有接口 Set<Class<?>> classes = new LinkedHashSet<>(ClassUtils.getAllInterfacesForClassAsSet(beanClass)); classes.add(beanClass); //遍歷所有的類和接口反射獲取到所有的方法 for (Class<?> clazz : classes) { Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz); for (Method m : methods) { allMethods.add(m); } } return allMethods; }
說明:
獲取類的所有接口:org.springframework.util.ClassUtils.getAllInterfacesForClassAsSet(Class<?>)
獲取類的所有方法:org.springframework.util.ReflectionUtils.getAllDeclaredMethods(Class<?>)
2)遍歷Advisor(通知者),取Advisor中的Pointcut(切入點)來匹配類、匹配方法
判斷bean實例是否要增強的代碼實現:
//在此判斷bean是否需要進行切面增強 private List<Advisor> getMatchedAdvisors(Object bean, String beanName) { if (CollectionUtils.isEmpty(advisors)) { return null; } // 得到類、類的所有方法 Class<?> beanClass = bean.getClass(); List<Method> allMethods = this.getAllMethodForClass(beanClass); // 存放匹配的Advisor的list List<Advisor> matchAdvisors = new ArrayList<>(); // 遍歷Advisor來找匹配的 for (Advisor ad : this.advisors) { if (ad instanceof PointcutAdvisor) { if (isPointcutMatchBean((PointcutAdvisor) ad, beanClass, allMethods)) { matchAdvisors.add(ad); } } } return matchAdvisors; } //獲取類的所有方法,包括繼承的父類和實現的接口里面的方法 private List<Method> getAllMethodForClass(Class<?> beanClass) { List<Method> allMethods = new LinkedList<>(); //獲取beanClass的所有接口 Set<Class<?>> classes = new LinkedHashSet<>(ClassUtils.getAllInterfacesForClassAsSet(beanClass)); classes.add(beanClass); //遍歷所有的類和接口反射獲取到所有的方法 for (Class<?> clazz : classes) { Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz); for (Method m : methods) { allMethods.add(m); } } return allMethods; } //判斷類及類的方法是否和切面匹配 private boolean isPointcutMatchBean(PointcutAdvisor pa, Class<?> beanClass, List<Method> methods) { Pointcut p = pa.getPointcut(); // 首先判斷類是否匹配 if (!p.matchsClass(beanClass)) { return false; } // 再判斷是否有方法匹配 for (Method method : methods) { if (p.matchsMethod(method, beanClass)) { return true; } } return false; }
3.2 代理增強
問題2:代理增強的邏輯是怎么樣的?
回憶一下JDK動態代理:
在運行時,對接口創建代理對象
生成代理類$Proxy0的方法:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
參數說明:
ClassLoader loader:類加載器
Class<?>[] interfaces:需要被代理的目標對象實現的接口,可以傳入多個
InvocationHandler h:功能增強的接口
功能增強的接口:
public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
參數說明:
Object proxy:被代理的目標對象(接口)
Method method:要調用的目標對象的方法
Object[] args:要調用的目標對象的方法的參數
回憶一下cglib動態代理:
在運行期為類、接口生成動態代理對象。 以達到不改動原類代碼而實現功能增強的目的
說明:
實現思想和前面的JDK動態代理一樣,只是使用了不同的API。
代理類由Enhancer生成,代理類實現被代理的類或者接口,特定的功能增強的實現MyMethodInterceptor實現MethodInterceptor接口,特定的功能增強實現MyMethodInterceptor里面持有被代理的類或者接口target
下面就把生成代理對象的部分和功能增強實現的部分分別實現
無論是JDK動態代理還是cglib動態代理都是生成代理對象,因此可以對這兩種代理進行抽象,先看下面的類圖
要生成代理對象,完成織入增強,JDK動態代理這里需要一些什么數據?
要實現的接口——要實現抽象生成代理對象的接口AopProxy和JDK動態代理生成代理對象的接口InvocationHandler
目標對象——需要增強的Bean
匹配的Advisors——Advice和Pointcut組成的切面去匹配被增強的Bean及Bean里面的方法
BeanFactory ?
要生成代理對象,完成織入增強,cglib動態代理這里需要一些什么數據?
要繼承的類 ?
要實現的接口——要實現抽象生成代理對象的接口AopProxy和cglib動態代理生成代理對象的接口MethodInterceptor
目標對象——需要增強的Bean
匹配的Advisors——Advice和Pointcut組成的切面去匹配被增強的Bean及Bean里面的方法
BeanFactory ?
構造參數類型 ?
構造參數 ?
下面看具體的代碼實現:
JDK動態代理和cglib動態代理抽象出公共部分的接口去獲取代理對象:
package com.study.spring.aop; /** * * @Description: JDK動態代理和cglib動態代理抽象出公共部分的接口去獲取代理對象 * @author leeSmall * @date 2018年12月2日 * */ public interface AopProxy { //獲取代理對象 Object getProxy(); //通過類加載器獲取代理對象 Object getProxy(ClassLoader classLoader); }
JDK動態代理實現:
package com.study.spring.aop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.dn.spring.aop.advisor.Advisor; import com.dn.spring.beans.BeanFactory; /** * * @Description: JDK動態AOP代理實現 * @author leeSmall * @date 2018年12月2日 * */ public class JdkDynamicAopProxy implements AopProxy, InvocationHandler { private static final Log logger = LogFactory.getLog(JdkDynamicAopProxy.class); private String beanName; private Object target; private List<Advisor> matchAdvisors; private BeanFactory beanFactory; public JdkDynamicAopProxy(String beanName, Object target, List<Advisor> matchAdvisors, BeanFactory beanFactory) { super(); this.beanName = beanName; this.target = target; this.matchAdvisors = matchAdvisors; this.beanFactory = beanFactory; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return AopProxyUtils.applyAdvices(target, method, args, matchAdvisors, proxy, beanFactory); } @Override public Object getProxy() { return this.getProxy(target.getClass().getClassLoader()); } @Override public Object getProxy(ClassLoader classLoader) { if (logger.isDebugEnabled()) { logger.debug("為" + target + "創建代理。"); } return Proxy.newProxyInstance(classLoader, target.getClass().getInterfaces(), this); } }
cglib動態代理實現:
package com.study.spring.aop; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.dn.spring.aop.advisor.Advisor; import com.dn.spring.beans.BeanDefinition; import com.dn.spring.beans.BeanFactory; import com.dn.spring.beans.DefaultBeanFactory; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * * @Description: cglib動態AOP代理實現 * @author leeSmall * @date 2018年12月2日 * */ public class CglibDynamicAopProxy implements AopProxy, MethodInterceptor { private static final Log logger = LogFactory.getLog(CglibDynamicAopProxy.class); private static Enhancer enhancer = new Enhancer(); private String beanName; private Object target; private List<Advisor> matchAdvisors; private BeanFactory beanFactory; public CglibDynamicAopProxy(String beanName, Object target, List<Advisor> matchAdvisors, BeanFactory beanFactory) { super(); this.beanName = beanName; this.target = target; this.matchAdvisors = matchAdvisors; 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 | SecurityException e) { } if (constructor != null) { return enhancer.create(); } //沒有無參構造函數時,從BeanDefinition里面獲取構造參數的類型和值進行增強 else { BeanDefinition bd = ((DefaultBeanFactory) beanFactory).getBeanDefinition(beanName); return enhancer.create(bd.getConstructor().getParameterTypes(), bd.getConstructorArgumentRealValues()); } } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { return AopProxyUtils.applyAdvices(target, method, args, matchAdvisors, proxy, beanFactory); } }
3.3 實現重要的增強邏輯
邏輯如下:
增強邏輯代碼應該寫在JDK動態代理的invoke方法和cglib動態代理的intercept方法里面
由於都是增強的代理,邏輯是一樣的就提取成一個公共的方法com.study.spring.aop.AopProxyUtils.applyAdvices(Object, Method, Object[], List<Advisor>, Object, BeanFactory)
JdkDynamicAopProxy代碼:
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return AopProxyUtils.applyAdvices(target, method, args, matchAdvisors, proxy, beanFactory); }
CglibDynamicAopProxy代碼:
@Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { return AopProxyUtils.applyAdvices(target, method, args, matchAdvisors, proxy, beanFactory); }
增強邏輯實現工具類AopProxyUtils代碼:
package com.dn.spring.aop; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import org.springframework.util.CollectionUtils; import com.dn.spring.aop.advisor.Advisor; import com.dn.spring.aop.advisor.PointcutAdvisor; import com.dn.spring.beans.BeanFactory; /** * * @Description: 增強邏輯實現工具類 * @author leeSmall * @date 2018年12月3日 * */ public class AopProxyUtils { /** * 對方法應用advices增強 * * @param target * @param method * @param args * @param matchAdvisors * @param proxy * @param beanFactory * @return * @throws Throwable */ public static Object applyAdvices(Object target, Method method, Object[] args, List<Advisor> matchAdvisors, Object proxy, BeanFactory beanFactory) throws Throwable { // 這里要做什么? // 1、獲取要對當前方法進行增強的advice List<Object> advices = AopProxyUtils.getShouldApplyAdvices(target.getClass(), method, matchAdvisors, beanFactory); // 2、如有增強的advice,就責任鏈式增強執行 if (CollectionUtils.isEmpty(advices)) { //沒有Advice就直接調用invoke方法 return method.invoke(target, args); } else { // 有Advice就責任鏈式執行增強 AopAdviceChainInvocation chain = new AopAdviceChainInvocation(proxy, target, method, args, advices); return chain.invoke(); } } /** * 獲取與方法匹配的切面的advices * * @param beanClass * @param method * @param matchAdvisors * @param beanFactory * @return * @throws Exception */ public static List<Object> getShouldApplyAdvices(Class<?> beanClass, Method method, List<Advisor> matchAdvisors, BeanFactory beanFactory) throws Throwable { if (CollectionUtils.isEmpty(matchAdvisors)) { return null; } List<Object> advices = new ArrayList<>(); for (Advisor ad : matchAdvisors) { if (ad instanceof PointcutAdvisor) { //如果當前方法和切入點匹配就是要加入增強功能的方法 if (((PointcutAdvisor) ad).getPointcut().matchsMethod(method, beanClass)) { advices.add(beanFactory.getBean(ad.getAdviceBeanName())); } } } return advices; } }
3.4 實現代理對象的創建
先實現JDK動態代理對象的方式的:
需要的參數:
要實現的接口 ——AopProxy和InvocationHandler
//創建代理對象 @Override public Object getProxy() { return this.getProxy(target.getClass().getClassLoader()); } //創建代理對象 @Override public Object getProxy(ClassLoader classLoader) { if (logger.isDebugEnabled()) { logger.debug("為" + target + "創建代理。"); } return Proxy.newProxyInstance(classLoader, target.getClass().getInterfaces(), this); }
實現cglib動態代理方式的:
需要的參數:
繼承的參數
實現的接口
Callback
構造參數類型
構造參數
問題:構造參數類型、構造參數從哪來?
創建對象實例時會有
傳遞構造參數類型和構造參數:
如何來傳遞創建bean實例時獲得的數據到初始化后的AOP中呢?
在BeanDefinition中用ThreadLocal保存創建bean實例時獲得的數據(構造參數類型、構造參數)
GenericBeanDefinition代碼:
//沒有無參構造函數時,傳遞構造參數的類型和值到cglib動態代理里面去獲取有參構造函數進行增強 private ThreadLocal<Object[]> realConstructorArgumentValues = new ThreadLocal<>(); @Override public Object[] getConstructorArgumentRealValues() { return realConstructorArgumentValues.get(); } @Override public void setConstructorArgumentRealValues(Object[] values) { realConstructorArgumentValues.set(values); }
DefaultBeanFactory代碼:
// 構造方法來構造對象 private Object createInstanceByConstructor(BeanDefinition bd) throws Throwable { try { Object[] args = this.getConstructorArgumentValues(bd); if (args == null) { return bd.getBeanClass().newInstance(); } else { bd.setConstructorArgumentRealValues(args); // 決定構造方法 Constructor<?> constructor = this.determineConstructor(bd, args); // 緩存構造函數由determineConstructor 中移到了這里,無論原型否都緩存,因為后面AOP需要用 bd.setConstructor(constructor); return constructor.newInstance(args); } } catch (SecurityException e1) { logger.error("創建bean的實例異常,beanDefinition:" + bd, e1); throw e1; } }
CglibDynamicAopProxy代碼:
//創建代理對象 @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 | SecurityException e) { } if (constructor != null) { return enhancer.create(); } //沒有無參構造函數時,從BeanDefinition里面獲取構造參數的類型和值進行增強 else { BeanDefinition bd = ((DefaultBeanFactory) beanFactory).getBeanDefinition(beanName); return enhancer.create(bd.getConstructor().getParameterTypes(), bd.getConstructorArgumentRealValues()); } }
2.5 AdvisorAutoProxyCreator中怎么使用動態代理AopProxy
把選擇使用哪個動態代理的邏輯交給工廠去判斷
AopProxyFactory代碼:
package com.study.spring.aop; import java.util.List; import com.dn.spring.aop.advisor.Advisor; import com.dn.spring.beans.BeanFactory; /** * * @Description: 工廠AopProxyFactory負責選擇使用哪個動態代理 * @author leeSamll * @date 2018年12月3日 * */ public interface AopProxyFactory { AopProxy createAopProxy(Object bean, String beanName, List<Advisor> matchAdvisors, BeanFactory beanFactory) throws Throwable; /** * 獲得默認的AopProxyFactory實例 * * @return AopProxyFactory */ static AopProxyFactory getDefaultAopProxyFactory() { return new DefaultAopProxyFactory(); } }
DefaultAopProxyFactory代碼:
package com.study.spring.aop; import java.util.List; import com.dn.spring.aop.advisor.Advisor; import com.dn.spring.beans.BeanFactory; /** * * @Description: 工廠AopProxyFactory的默認實現 負責選擇使用哪個動態代理 * @author leeSamll * @date 2018年12月3日 * */ public class DefaultAopProxyFactory implements AopProxyFactory { @Override public AopProxy createAopProxy(Object bean, String beanName, List<Advisor> matchAdvisors, BeanFactory beanFactory) throws Throwable { // 是該用jdk動態代理還是cglib? if (shouldUseJDKDynamicProxy(bean, beanName)) { return new JdkDynamicAopProxy(beanName, bean, matchAdvisors, beanFactory); } else { return new CglibDynamicAopProxy(beanName, bean, matchAdvisors, beanFactory); } } //默認使用cglib private boolean shouldUseJDKDynamicProxy(Object bean, String beanName) { // 如何判斷? // 這樣可以嗎:有實現接口就用JDK,沒有就用cglib? return false; } }
AdvisorAutoProxyCreator代碼:
//創建代理對象增強 private Object createProxy(Object bean, String beanName, List<Advisor> matchAdvisors) throws Throwable { // 通過AopProxyFactory工廠去完成選擇、和創建代理對象的工作。 return AopProxyFactory.getDefaultAopProxyFactory().createAopProxy(bean, beanName, matchAdvisors, beanFactory) .getProxy(); }
完整源碼獲取地址:
https://github.com/leeSmall/FrameSourceCodeStudy/tree/master/spring-v3