問題1:AOP是什麼?java
Aspect Oriented Programming 面向切面編程,在不改變類的代碼的狀況下,對類方法進行功能加強。git
問題2:咱們須要作什麼?github
在咱們的框架中要向使用用戶提供AOP功能,讓他們能夠經過AOP技術實現對類方法進行功能加強。正則表達式
從"Aspect Oriented Programming 面向切面編程,在不改變類的代碼的狀況下,對類方法進行功能加強"這句話咱們能獲得下面的這些信息:spring
咱們先來看一下下面的這張圖express
說明:apache
程序運行時會調用不少方法,調用的不少方法就叫作Join points(鏈接點,能夠被選擇來進行加強的方法點),在方法的前或者後選擇一個地方來切入,切入的的地方就叫作Pointcut(切入點,選擇加強的方法),而後把要加強的功能(Advice)加入到切入點所在的位置。Advice和Pointcut組成一個切面(Aspect)編程
AOP的幾個概念:緩存
Advice、Pointcut、Weaving的特色:app
Advice(功能加強):
1)用戶性:由用戶提供加強功能的邏輯代碼
2)變化的:不一樣的加強需求,會有不一樣的邏輯
3)可選時機:可選擇在方法前、後、異常時進行功能加強
4)多重的:同一個切入點上能夠有多重加強
Pointcut(切入點):
1)用戶性:由用戶來指定
2)變化的:用戶可靈活指定
3)多點性:用戶能夠選擇在多個點上進行功能加強
Weaving(織入):
1)無侵入性,由於不改變原類的代碼
2)咱們在框架中實現
咱們將要分析Advice、Pointcut、Aspect這三個東西
Advice是由用戶來提供,咱們來使用,它是多變得。
問題1:咱們如何能識別用戶提供的東西?用戶在咱們寫好框架之後使用咱們的框架。
問題2:如何讓咱們的代碼隔絕用戶提供的多變?
解決方法:
咱們定義一套標準接口,用戶經過實現接口來提供它們不一樣的邏輯。
爲了應對變化,這裏使用到了設計原則:面向接口編程
Advice的特色:可選時機,可選擇在方法前、後、異常時進行功能加強
1)有的Advice是在方法執行前進行加強——前置加強
2)有的Advice是在方法執行後進行加強——後置加強
3)有的Advice會在方執行先後都進行加強——環繞加強
4)有的Advice則只是在方法執行拋出異常時進行加強——異常處理加強
問題1:咱們須要作什麼?
定義標準接口方法,讓用戶能夠實現它,提供各類加強。
問題2:這四種加強所需的參數同樣嗎?
下面咱們來一個一個的分析
前置加強:在方法執行前進行加強
問題1:它可能須要什麼參數?
目的是對方法進行加強,應該須要的是方法相關的信息。
問題2:運行時,方法有哪些信息?
方法自己 Method
方法屬於哪一個類 Object
方法的參數 Object [ ]
方法的返回值
...........
問題3:前置加強可能須要什麼參數?
方法自己 Method
方法屬於哪一個類 Object
方法的參數 Object [ ]
問題3:前置加強的返回值是什麼?
在方法執行前進行加強,不須要返回值
後置加強:在方法執行後進行加強
問題1:後置加強可能須要什麼參數?
方法自己 Method
方法屬於哪一個類 Object
方法的參數 Object [ ]
方法的返回值
問題2:後置加強的返回值是什麼?
在方法執行後進行加強,不須要返回值
環繞加強:方法執行先後進行加強(包裹方法進行加強)
問題1:它可能須要什麼參數?
方法自己 Method
方法屬於哪一個類 Object
方法的參數 Object [ ]
問題2:環繞加強的返回值是什麼?
方法被它包裹,也即方法將由它來執行,它須要返回方法的返回值。Object
異常處理加強:捕獲方法執行時的異常,進行加強處理。
問題1:它可能須要什麼參數?
異常信息
問題2:進行異常處理加強須要包裹方法嗎?
須要,把執行代碼用try包起來,捕獲到哪一個異常就在哪裏進行加強
問題3:那它能否在環繞中實現?
能夠
問題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; }
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本就是切面編程的組件,也是能夠的
AspectJ是什麼?
AspectJ是java裏面切面編程的庫,可以幫助咱們完成預編譯時的代碼加強,和eclispe配合使用能夠生成相關的字節碼。咱們在AOP裏面只使用了他的表達式解析匹配相關的API
AspectJ的Pointcut表達式是用來表示應該在哪一個類的哪一個方法進行切入進行方法加強的。
語法以下:
示例:
* com.study.design.mode.samples.proxy.Girl.*(..)
下面開始對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)組成一個切面
上面的使用仍是不太好,這個時候咱們須要爲上面的使用抽象出一個接口,使得用戶的使用更加簡單!!!請繼續看下面的內容
爲用戶提供更簡單的外觀,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:在何時作織入?
建立bean實例的時候,在bean初始化完成後,再對其進行加強
問題2:如何肯定bean要加強?
對bean類及其方法挨個匹配用戶指定的切面,若是有切面匹配就是要加強的
問題3:如何織入
代理
整理一下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加強處理接口:
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); }
問題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; }
問題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); } }
邏輯以下:
加強邏輯代碼應該寫在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 { // 這裏要作什麼? // 一、獲取要對當前方法進行加強的advice List<Object> advices = AopProxyUtils.getShouldApplyAdvices(target.getClass(), method, matchAdvisors, beanFactory); // 二、若有加強的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; } }
先實現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()); } }
把選擇使用哪一個動態代理的邏輯交給工廠去判斷
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