javascript
Spring中AOP相关的API及源码解析,原来AOP是这样子的
前言
之所以寫這么一篇文章主要是因為下篇文章將結束Spring啟動整個流程的分析,從解析配置到創建對象再到屬性注入最后再將創建好的對象初始化成為一個真正意義上的Bean。因為下篇文章會涉及到AOP,所以提前單獨將AOP的相關API及源碼做一次解讀,這樣可以降低閱讀源碼的障礙,話不多說,我們進入正文!
一個使用API創建代理的例子
在進入API分析前,我們先通過兩個例子體會下如何使用API的方式來創建一個代理對象,對應示例如下:
定義通知
切點
目標類
測試代碼
這里我就不將測試結果放出來了,大家可以先自行思考這段程序將輸出什么。接下來我們就來分析上面這段程序中所涉及到的API,通過這些API的學習相信大家可以徹底理解上面這段代碼。
API介紹
Pointcut(切點)
對應接口定義如下:
public?interface?Pointcut?{//?ClassFilter,在類級別進行過濾ClassFilter?getClassFilter();//?MethodMatcher,在方法級別進行過濾MethodMatcher?getMethodMatcher();//?一個單例對象,默認匹配所有Pointcut?TRUE?=?TruePointcut.INSTANCE;}切點的主要作用是定義通知所要應用到的類跟方法,上面的接口定義也很明顯的體現了這一點,我們可以將其拆分成為兩個部分
-
ClassFilter,接口定義如下:
ClassFilter的主要作用是在類級別上對通知的應用進行一次過濾,如果它的match方法對任意的類都返回true的話,說明在類級別上我們不需要過濾,這種情況下,通知的應用,就完全依賴MethodMatcher的匹配結果。
-
MethodMatcher,接口定義如下:
MethodMatcher中一共有三個核心方法
-
matches(Method method, @Nullable Class<?> targetClass),這個方法用來判斷當前定義的切點跟目標類中的指定方法是否匹配,它可以在創建代理的時候就被調用,從而決定是否需要進行代理,這樣就可以避免每次方法執行的時候再去做判斷
-
isRuntime(),如果這個方法返回true的話,意味著每次執行方法時還需要做一次匹配
-
matches(Method method, @Nullable Class<?> targetClass, Object... args),當之前的isRuntime方法返回true時,會調用這個方法再次進行一次判斷,返回false的話,意味這個不對這個方法應用通知
Advice(通知)
環繞通知(Interception Around Advice)
接口定義如下:
public?interface?MethodInterceptor?extends?Interceptor?{Object?invoke(MethodInvocation?invocation)?throws?Throwable; }在上面接口定義的invoke方法中,MethodInvocation就是當前執行的方法,當我們調用invocation.proceed就是在執行當前的這個方法,基于此,我們可以在方法的執行前后去插入我們自定義的邏輯,比如下面這樣
//?執行前的邏輯 doSomeThingBefore(); Object?var?=?invocation.proceed; doSomeThingAfter(); //?執行后的邏輯 retrun?var;前置通知(Before Advice)
public?interface?MethodBeforeAdvice?extends?BeforeAdvice?{void?before(Method?m,?Object[]?args,?Object?target)?throws?Throwable; }跟環繞通知不同的是,這個接口中定義的方法的返回值是void,所以前置通知是無法修改方法的返回值的。
如果在前置通知中發生了異常,那么會直接終止目標方法的執行以及打斷整個攔截器鏈的執行
后置通知(After Returning Advice)
public?interface?AfterReturningAdvice?extends?Advice?{void?afterReturning(Object?returnValue,?Method?m,?Object[]?args,?Object?target)throws?Throwable; }后置通知相比較于前置通知,主要有以下幾點不同
-
后置通知可以訪問目標方法的返回值,但是不能修改
-
后置通知是在方法執行完成后執行
異常通知(Throws Advice)
public?interface?ThrowsAdvice?extends?AfterAdvice?{}異常通知中沒有定義任何方法,它更像一個標記接口。我們在定義異常通知時需要實現這個接口,同時方法的簽名也有要求
方法名稱必須是afterThrowing
方法的參數個數必須是1個或者4個,如下:
我們可以在一個異常通知中定義多個方法,在后續的源碼分析中我們會發現,這些方法最終會被注冊成對應的異常的handler,像下面這樣
public?static?class?CombinedThrowsAdvice?implements?ThrowsAdvice?{public?void?afterThrowing(RemoteException?ex)?throws?Throwable?{//?Do?something?with?remote?exception}public?void?afterThrowing(Method?m,?Object[]?args,?Object?target,?ServletException?ex)?{//?Do?something?with?all?arguments} }引入通知(Introduction Advice)
引入通知的主要作用是可以讓生成的代理類實現額外的接口。例如在上面的例子中,我們為DmzService創建一個代理對象,同時為其定義了一個引入通知
public?class?DmzIntroductionAdvice?extends?DelegatingIntroductionInterceptor?implements?Runnable?{@Overridepublic?void?run()?{System.out.println("running!!!!");} }在這個引入通知中,我們為其引入了一個新的需要實現的接口Runnable,同時通知本身作為這個接口的實現類。
通過這個引入通知,我們可以將生成的代理類強轉成Runnable類型然后執行其run方法,同時,run方法也會被前面定義的前置通知,后置通知等攔截。
為了更好的了解引入通知,我們來需要了解下DelegatingIntroductionInterceptor這個類。見名知意,這個類就是一個委托引入攔截器,因為我們要為代理類引入新的接口,因為著我們要提供具體的實現的邏輯,而具體的實現的邏輯就可以被委托給這個DelegatingIntroductionInterceptor。
我們可以看看它的源碼
public?class?DelegatingIntroductionInterceptor?extends?IntroductionInfoSupportimplements?IntroductionInterceptor?{//?實際實現了引入邏輯的類@Nullableprivate?Object?delegate;//?對外提供了一個帶參的構造函數,通過這個構造函數我們可以傳入一個//?具體的實現類public?DelegatingIntroductionInterceptor(Object?delegate)?{init(delegate);}//?對子類暴露了一個空參的構造函數,默認將自身作為實現了引入邏輯的委托類//?我們上面的例子中就是使用的這種方法protected?DelegatingIntroductionInterceptor()?{init(this);}//?對這個類進行初始化,要通過實際的實現類來找到具體要實現的接口private?void?init(Object?delegate)?{Assert.notNull(delegate,?"Delegate?must?not?be?null");this.delegate?=?delegate;//?找到delegate所有實現的接口implementInterfacesOnObject(delegate);//?因為我們可能會將DelegatingIntroductionInterceptor本身作為委托者//?Spring的設計就是不對外暴露這兩個接口//?如果將其暴露,意味著我們可以將代理類強轉成這種類型suppressInterface(IntroductionInterceptor.class);suppressInterface(DynamicIntroductionAdvice.class);}//?引入通知本身也是基于攔截器實現的,當執行一個方法時需要判斷這個方法//?是不是被引入的接口中定義的方法,如果是的話,那么不能調用目標類的方法//?而要調用委托類的方法public?Object?invoke(MethodInvocation?mi)?throws?Throwable?{if?(isMethodOnIntroducedInterface(mi))?{Object?retVal?=?AopUtils.invokeJoinpointUsingReflection(this.delegate,?mi.getMethod(),?mi.getArguments());//?這里是處理一種特殊情況,方法的返回值是this的時候//?這里應該返回代理類if?(retVal?==?this.delegate?&&?mi?instanceof?ProxyMethodInvocation)?{Object?proxy?=?((ProxyMethodInvocation)?mi).getProxy();if?(mi.getMethod().getReturnType().isInstance(proxy))?{retVal?=?proxy;}}//?其余情況下直接將委托類的執行結果返回return?retVal;}//?執行到這里說明不是引入的方法,這是Spring提供了一個擴展邏輯//?正常來說這個類只會處理引入的邏輯,通過這個方法可以對目標類中的方法做攔截//?不常用return?doProceed(mi);}protected?Object?doProceed(MethodInvocation?mi)?throws?Throwable?{return?mi.proceed();}}通過查看這個類的源碼我們可以發現,所謂的引入其實就是在方法執行的時候加了一層攔截,當判斷這個方法是被引入的接口提供的方法的時候,那么就執行委托類中的邏輯而不是目標類中的方法
關于通知的總結
通過上文的分析我們可以發現,通知總共可以分為這么幾類
普通的通知(前置,后置,異常等,沒有實現MethodInterceptor接口)
環繞通知(實現了MethodInterceptor接口)
引入通知(需要提供額外的引入的信息,實現了MethodInterceptor接口)
上面的分類并不標準,只是為了方便大家記憶跟理解,雖然我們普通的通知沒有直接實現MethodInterceptor接口,但其實它的底層也是依賴于攔截器來完成的,大家可以看看下面這個類
class?MethodBeforeAdviceAdapter?implements?AdvisorAdapter,?Serializable?{@Overridepublic?boolean?supportsAdvice(Advice?advice)?{return?(advice?instanceof?MethodBeforeAdvice);}//?根據傳入的一個前置通知,創建一個對應的攔截器@Overridepublic?MethodInterceptor?getInterceptor(Advisor?advisor)?{MethodBeforeAdvice?advice?=?(MethodBeforeAdvice)?advisor.getAdvice();return?new?MethodBeforeAdviceInterceptor(advice);}}public?class?MethodBeforeAdviceInterceptor?implements?MethodInterceptor,?BeforeAdvice,?Serializable?{private?final?MethodBeforeAdvice?advice;public?MethodBeforeAdviceInterceptor(MethodBeforeAdvice?advice)?{Assert.notNull(advice,?"Advice?must?not?be?null");this.advice?=?advice;}//?實際上還是利用攔截器,在方法執行前調用了通知的before方法完成了前置通知@Overridepublic?Object?invoke(MethodInvocation?mi)?throws?Throwable?{this.advice.before(mi.getMethod(),?mi.getArguments(),?mi.getThis());return?mi.proceed();}}Advisor (綁定通知跟切點)
一個Advisor實際上就是一個綁定在指定切點上的通知。在前面的例子我們可以發現,有兩種添加通知的方式
//?一個Advisor代表的是一個已經跟指定切點綁定了的通知 //?在這個例子中意味著環繞通知不會作用到toString方法上 Advisor?advisor?=?new?DefaultPointcutAdvisor(new?DmzPointcut(),?new?DmzAroundAdvice());//?添加一個綁定了指定切點的環繞通知 proxyFactory.addAdvisor(advisor);//?添加一個返回后的通知 proxyFactory.addAdvice(new?DmzAfterReturnAdvice());一種是直接添加了一個Advisor,還有一種是添加一個Advice,后者也會被轉換成一個Advisor然后再進行添加,沒有指定切點的通知是沒有任何意義的
public?void?addAdvice(Advice?advice)?throws?AopConfigException?{int?pos?=?this.advisors.size();//?默認添加到集合的最后一個位置addAdvice(pos,?advice); }//?這個方法添加通知 public?void?addAdvice(int?pos,?Advice?advice)?throws?AopConfigException?{Assert.notNull(advice,?"Advice?must?not?be?null");//?如果是一個引入通知,那么構建一個DefaultIntroductionAdvisor//?DefaultIntroductionAdvisor會匹配所有類if?(advice?instanceof?IntroductionInfo)?{addAdvisor(pos,?new?DefaultIntroductionAdvisor(advice,?(IntroductionInfo)?advice));}//?不能直接添加一個不是IntroductionInfo的DynamicIntroductionAdvice(動態引入通知)else?if?(advice?instanceof?DynamicIntroductionAdvice)?{throw?new?AopConfigException("DynamicIntroductionAdvice?may?only?be?added?as?part?of?IntroductionAdvisor");}else?{//?如果是普通的通知,那么會創建一個DefaultPointcutAdvisor//?DefaultPointcutAdvisor所定義的切點會匹配所有類以及所有方法addAdvisor(pos,?new?DefaultPointcutAdvisor(advice));} }ProxyCreatorSupport
這個類的主要作用是為創建一個AOP代理對象提供一些功能支持,通過它的getAopProxyFactory能獲取一個創建代理對象的工廠。
//?這里我只保留了這個類中的關鍵代碼 public?class?ProxyCreatorSupport?extends?AdvisedSupport?{?private?AopProxyFactory?aopProxyFactory;//?空參構造,默認會創建一個DefaultAopProxyFactory//?通過這個ProxyFactory可以創建一個cglib代理或者jdk代理public?ProxyCreatorSupport()?{this.aopProxyFactory?=?new?DefaultAopProxyFactory();}//?通過這個方法可以創建一個具體的代理對象protected?final?synchronized?AopProxy?createAopProxy()?{if?(!this.active)?{activate();}//?實際就是使用DefaultAopProxyFactory來創建一個代理對象//?可以看到在調用createAopProxy方法時,傳入的參數是this//?這是因為ProxyCreatorSupport本身就保存了創建整個代理對象所需要的配置信息return?getAopProxyFactory().createAopProxy(this);} }另外通過上面的UML類圖還能看到,ProxyCreatorSupport繼承了AdvisedSupport,AdvisedSupport繼承了ProxyConfig。
ProxyConfig
其中ProxyConfig是所有的AOP代理工廠的父類,它包含了創建一個AOP代理所需要的基礎的通用的一些配置信息
//?這里省略了一些getter跟setter方法 public?class?ProxyConfig?implements?Serializable?{//?是否開啟cglib代理,默認不開啟使用jdk動態代理private?boolean?proxyTargetClass?=?false;//?是否啟用優化,默認為false,按照官網對這個參數的解釋//?這個優化是針對cglib,如果設計為true的話,會做一些侵入性的優化//?是否開啟在jdk代理的情況下沒有影響//?官網中特地說明了,除非對cglib的優化非常了解,否則不要開啟這個參數private?boolean?optimize?=?false;//?生成的代理類是否需要實現Advised接口,這個接口可以向外提供操作通知的方法//?如果為false會實現//?為true的話,不會實現boolean?opaque?=?false;//?是否將當前的配置類暴露到一個線程上下文中,如果設置為true的話//?可以通過AopContext.currentProxy()來獲取到當前的代理對象boolean?exposeProxy?=?false;//?標志著是否凍結整個配置,如果凍結了,那么配置信息將不允許修改private?boolean?frozen?=?false; }AdvisedSupport
當我們為某個對象創建代理時,除了需要上面的ProxyConfig提供的一些基礎配置外,起碼還需要知道
需要執行的通知是哪些?
目標對象是誰?
創建出來的代理需要實現哪些接口?
而這些配置信息是由AdvisedSupport提供的,AdvisedSupport本身實現了Advised接口,Advised接口定義了管理通知的方法。
在了解了上面的API后我們來看看Spring提供了幾種創建AOP代理的方式
ProxyFactoryBean
ProxyFactory
Auto-proxy
ProxyFactoryBean的方式創建AOP代理
使用示例
<?xml?version="1.0"?encoding="UTF-8"?> <beans?xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans?http://www.springframework.org/schema/beans/spring-beans.xsd"><bean?class="com.dmz.spring.initalize.service.DmzService"?name="dmzService"/><bean?id="aroundAdvice"?class="com.dmz.spring.initalize.aop.advice.DmzAroundAdvice"/><bean?id="dmzProxy"class="org.springframework.aop.framework.ProxyFactoryBean"><!----><property?name="proxyInterfaces"?value="java.lang.Runnable"/><property?name="proxyTargetClass"?value="true"/><property?name="target"?ref="dmzService"/><property?name="interceptorNames"><list><value>aroundAdvice</value></list></property></bean></beans> //?目標類 public?class?DmzService?{@Overridepublic?String?toString()?{System.out.println("dmzService?toString?invoke");return?"dmzService";}public?void?testAop(){System.out.println("testAop?invoke");} }//?通知 public?class?DmzAroundAdvice?implements?MethodInterceptor?{@Overridepublic?Object?invoke(MethodInvocation?invocation)?throws?Throwable?{System.out.println("aroundAdvice?invoked");return?invocation.proceed();} }public?class?SourceMain?{public?static?void?main(String[]?args)?{ClassPathXmlApplicationContext?cc?=new?ClassPathXmlApplicationContext("application-init.xml");DmzService?dmzProxy?=?((DmzService)?cc.getBean("dmzProxy"));dmzProxy.testAop();} }ProxyFactoryBean介紹
跟普通的FactoryBean一樣,這個類的主要作用就是通過getObject方法能夠獲取一個Bean,不同的是這個類獲取到的是代理后的Bean。
我們查看這個類的繼承關系可以發現
這個類除了實現了FactoryBean接口以及一些Aware接口外,額外還繼承了ProxyCreatorSupport類。它是一個factoryBean,所以我們重點就關注它的getObject方法即可。
public?Object?getObject()?throws?BeansException?{//?初始化通知鏈//?這里主要就是將在XML中配置的通知添加到//?AdvisedSupport管理的配置中去initializeAdvisorChain();if?(isSingleton())?{//?如果是單例的,那么獲取一個單例的代理對象return?getSingletonInstance();}else?{if?(this.targetName?==?null)?{logger.warn("Using?non-singleton?proxies?with?singleton?targets?is?often?undesirable.?"?+"Enable?prototype?proxies?by?setting?the?'targetName'?property.");}//?如果是原型的,獲取一個原型的代理對象return?newPrototypeInstance();} }關于這段代碼就不做過多分析了,它其實就兩步(不管是哪種方式創建代理,都分為這兩步)
完善創建代理需要的配置信息
創建代理
其中配置信息分為兩部分,其一是AppConfig管理的通用的配置信息,其二是AdvisedSupport管理的通知信息。通用的配置信息我們可以直接在XML中配置,例如在上面的例子中我們就配置了proxyTargetClass屬性,而通知信息即使我們在XML中配置了也還需要做一層轉換,在前面我們也提到過了,所有的Advice都會被轉換成Advisor添加到配置信息中。
ProxyFactory的方式創建AOP代理
使用示例(略,見開頭)
ProxyFactory介紹
從上面我們可以看出,ProxyFactory也繼承自ProxyCreatorSupport,從之前的例子我們也能感受到,使用它的API來創建一個代理對象也是要先去設置相關的配置信息,最后再調用創建代理的方法
我們之后要分析的自動代理內部就是通過創建了一個ProxyFactory來獲取代理對象的。
我們可以對比下ProxyFactoryBean跟ProxyFactory在創建代理對象時的代碼
-
ProxyFactory
-
ProxyFactoryBean
綜上,我們可以得出結論,不管是通過哪種方式創建AOP代理,核心代碼就一句
createAopProxy().getProxy()這句代碼也是我們接下來源碼分析的重點
Auto-proxy(實現自動AOP代理)
自動代理機制的實現其實很簡單,就是通過Bean的后置處理器,在創建Bean的最后一步對Bean進行代理,并將代理對象放入到容器中。
實現自動代理的核心類就是AbstractAutoProxyCreator。我們來看看它的繼承關系
為了更好的體會自動代理的作用,我們對它的三個具體的實現類來進行分析,分別是
BeanNameAutoProxyCreator
DefaultAdvisorAutoProxyCreator
AnnotationAwareAspectJAutoProxyCreator
BeanNameAutoProxyCreator
使用示例
<?xml?version="1.0"?encoding="UTF-8"?> <beans?xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans?http://www.springframework.org/schema/beans/spring-beans.xsd"><bean?class="com.dmz.spring.initalize.service.DmzService"?name="dmzService"/><bean?id="aroundAdvice"?class="com.dmz.spring.initalize.aop.advice.DmzAroundAdvice"/><bean?id="beforeAdvice"?class="com.dmz.spring.initalize.aop.advice.DmzBeforeAdvice"/><!--使用很簡單,只要配置一個BeanNameAutoProxyCreator即可--><bean?class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"?name="autoProxyCreator"><!--使用cglib代理--><property?name="proxyTargetClass"?value="true"/><!--對所有以dmz開頭的bean進行自動代理--><property?name="beanNames"?value="dmz*"/><!--添加兩個通知--><property?name="interceptorNames"><list><value>beforeAdvice</value><value>aroundAdvice</value></list></property></bean></beans> public?class?SourceMain?{public?static?void?main(String[]?args)?{ClassPathXmlApplicationContext?cc?=new?ClassPathXmlApplicationContext("application-init.xml");DmzService?dmzProxy?=?((DmzService)?cc.getBean("dmzService"));dmzProxy.testAop();} } //?程序打印: //?before?invoke?method?[testAop],aop?before?logic?invoked //?aroundAdvice?invoked //?testAop?invokeDefaultAdvisorAutoProxyCreator
使用示例
在上面例子的基礎上我們要修改配置文件,如下:
<?xml?version="1.0"?encoding="UTF-8"?> <beans?xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans?http://www.springframework.org/schema/beans/spring-beans.xsd"><bean?class="com.dmz.spring.initalize.service.DmzService"?name="dmzService"/><bean?id="aroundAdvice"?class="com.dmz.spring.initalize.aop.advice.DmzAroundAdvice"/><bean?id="beforeAdvice"?class="com.dmz.spring.initalize.aop.advice.DmzBeforeAdvice"/><bean?class="org.springframework.aop.support.DefaultPointcutAdvisor"?id="dmzBeforeAdvisor"><property?name="advice"?ref="beforeAdvice"/></bean><bean?class="org.springframework.aop.support.DefaultPointcutAdvisor"?id="dmzAroundAdvisor"><property?name="advice"?ref="aroundAdvice"/></bean><bean?class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"id="advisorAutoProxyCreator"><!--這兩個參數標明了我們要使用所有以dmz開頭的Advisor類型的通知這里必須配置是Advisor,不能是Advice或者interceptor,可以看到DefaultAdvisorAutoProxyCreator跟BeanNameAutoProxyCreator的區別在于BeanNameAutoProxyCreator需要指定要被代理的bean的名稱,而DefaultAdvisorAutoProxyCreator不需要,它會根據我們傳入的Advisor獲取到需要被代理的切點--><property?name="usePrefix"?value="true"/><property?name="advisorBeanNamePrefix"?value="dmz"/><property?name="proxyTargetClass"?value="true"/></bean> </beans>測試代碼就不放了,大家可以自行測試,肯定是沒問題的
AnnotationAwareAspectJAutoProxyCreator
我們正常在使用AOP的時候都會在配置類上添加一個@EnableAspectJAutoProxy注解,這個注解干了什么事呢?
實際就是向容器中注冊了一個AnnotationAwareAspectJAutoProxyCreator。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented //?這里導入了一個類 @Import(AspectJAutoProxyRegistrar.class) public?@interface?EnableAspectJAutoProxy?{boolean?proxyTargetClass()?default?false;boolean?exposeProxy()?default?false;}通過@EnableAspectJAutoProxy導入了一個AspectJAutoProxyRegistrar,這個類會向容器中注冊一個AnnotationAwareAspectJAutoProxyCreator,對應源碼如下:
class?AspectJAutoProxyRegistrar?implements?ImportBeanDefinitionRegistrar?{@Overridepublic?void?registerBeanDefinitions(AnnotationMetadata?importingClassMetadata,?BeanDefinitionRegistry?registry)?{//?在這里完成的注冊//?最終會調用到AopUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法//?完成AnnotationAwareAspectJAutoProxyCreator這個bd的注冊AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);//?解析注解的屬性// proxyTargetClass:為true的話開啟cglib代理,默認為jdk代理// exposeProxy:是否將代理對象暴露到線程上下文中AnnotationAttributes?enableAspectJAutoProxy?=AnnotationConfigUtils.attributesFor(importingClassMetadata,?EnableAspectJAutoProxy.class);if?(enableAspectJAutoProxy?!=?null)?{if?(enableAspectJAutoProxy.getBoolean("proxyTargetClass"))?{AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);}if?(enableAspectJAutoProxy.getBoolean("exposeProxy"))?{AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);}}}}前面已經說過了,自動代理機制實際上就是Spring在內部new了一個ProxyFactory,通過它創建了一個代理對象。對應的代碼就在AbstractAutoProxyCreator中的createProxy方法內,源碼如下:
protected?Object?createProxy(Class<?>?beanClass,?@Nullable?String?beanName,@Nullable?Object[]?specificInterceptors,?TargetSource?targetSource)?{if?(this.beanFactory?instanceof?ConfigurableListableBeanFactory)?{AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory)?this.beanFactory,?beanName,?beanClass);}//?看到了吧,這里創建了一個proxyFactoryProxyFactory?proxyFactory?=?new?ProxyFactory();proxyFactory.copyFrom(this);if?(!proxyFactory.isProxyTargetClass())?{if?(shouldProxyTargetClass(beanClass,?beanName))?{proxyFactory.setProxyTargetClass(true);}else?{evaluateProxyInterfaces(beanClass,?proxyFactory);}}Advisor[]?advisors?=?buildAdvisors(beanName,?specificInterceptors);proxyFactory.addAdvisors(advisors);proxyFactory.setTargetSource(targetSource);customizeProxyFactory(proxyFactory);proxyFactory.setFrozen(this.freezeProxy);if?(advisorsPreFiltered())?{proxyFactory.setPreFiltered(true);}//?通過proxyFactory來創建一個代理對象return?proxyFactory.getProxy(getProxyClassLoader()); }關于這個類的執行流程在下篇文章中我再詳細介紹,接下來我們要分析的就是具體創建AOP代理的源碼了。對應的核心源碼就是我們之前所提到的
createAopProxy().getProxy();這行代碼分為兩步,我們逐步分析
調用AopProxyFactory的createAopProxy()方法獲取一個AopProxy對象
調用AopProxy對象的getProxy()方法
核心源碼分析
createAopProxy方法分析
AopProxyFactory在Spring中只有一個默認的實現類,就是DefaultAopProxyFactory,它的對應的createAopProxy的是實現代碼如下:
public?class?DefaultAopProxyFactory?implements?AopProxyFactory,?Serializable?{//?就是通過AOP相關的配置信息來決定到底是使用cglib代理還是jdk代理@Overridepublic?AopProxy?createAopProxy(AdvisedSupport?config)?throws?AopConfigException?{//?如果開啟了優化,或者ProxyTargetClass設置為true//?或者沒有提供代理類需要實現的接口,那么使用cglib代理//?在前面分析參數的時候已經說過了//?默認情況下Optimize都為false,也不建議設置為true,因為會進行一些侵入性的優化//?除非你對cglib的優化非常了解,否則不建議開啟if?(config.isOptimize()?||?config.isProxyTargetClass()?||?hasNoUserSuppliedProxyInterfaces(config))?{Class<?>?targetClass?=?config.getTargetClass();if?(targetClass?==?null)?{throw?new?AopConfigException("TargetSource?cannot?determine?target?class:?"?+"Either?an?interface?or?a?target?is?required?for?proxy?creation.");}//?需要注意的是,如果需要代理的類本身就是一個接口//?或者需要被代理的類本身就是一個通過jdk動態代理生成的類//?那么不管如何設置都會使用jdk動態代理if?(targetClass.isInterface()?||?Proxy.isProxyClass(targetClass))?{return?new?JdkDynamicAopProxy(config);}return?new?ObjenesisCglibAopProxy(config);}//?否則都是jdk代理else?{return?new?JdkDynamicAopProxy(config);}}//?判斷是否提供代理類需要實現的接口private?boolean?hasNoUserSuppliedProxyInterfaces(AdvisedSupport?config)?{Class<?>[]?ifcs?=?config.getProxiedInterfaces();return?(ifcs.length?==?0?||?(ifcs.length?==?1?&&?SpringProxy.class.isAssignableFrom(ifcs[0])));}}getProxy方法分析
從對createAopProxy方法的分析可以看到,我們要么執行的是ObjenesisCglibAopProxy中的getProxy方法,要么就是JdkDynamicAopProxy的getProxy方法,二者的區別在于一個是通過cglib的方式生成代理對象,而后者則是通過jdk的方式生成動態代理。
這里我只分析一個JdkDynamicAopProxy,首先我們來看看這個類的繼承關系
?希望你之前已經閱讀過
原創 動態代理學習(一)自己動手模擬JDK動態代理
原創 動態代理學習(二)JDK動態代理源碼分析
”可以看到這個類本身就是一個InvocationHandler,這意味著當調用代理對象中的方法時,最終會調用到JdkDynamicAopProxy的invoke方法。
所以對于這個類我們起碼應該關注兩個方法
getProxy方法
invoke方法
getProxy方法源碼如下:
public?Object?getProxy(@Nullable?ClassLoader?classLoader)?{if?(logger.isDebugEnabled())?{logger.debug("Creating?JDK?dynamic?proxy:?target?source?is?"?+?this.advised.getTargetSource());}//?這里獲取到代理類需要實現的所有的接口Class<?>[]?proxiedInterfaces?=?AopProxyUtils.completeProxiedInterfaces(this.advised,?true);//?需要明確是否在接口定義了hashCode以及equals方法//?如果接口中沒有定義,那么在調用代理對象的equals方法的時候//?如果兩個對象相等,那么意味著它們的目標對象,通知以及實現的接口都相同findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);return?Proxy.newProxyInstance(classLoader,?proxiedInterfaces,?this); }我們再來看看到底是怎么獲取到需要實現的接口的
static?Class<?>[]?completeProxiedInterfaces(AdvisedSupport?advised,?boolean?decoratingProxy)?{//?第一步:獲取在配置中指定的需要實現的接口Class<?>[]?specifiedInterfaces?=?advised.getProxiedInterfaces();//?第二步:如果沒有指定需要實現的接口,但是需要代理的目標類本身就是一個接口//?那么將其添加到代理類需要實現的接口的集合中//?如果目標類本身不是一個接口,但是是經過jdk代理后的一個類//?那么獲取這個代理后的類所有實現的接口,并添加到需要實現的接口集合中if?(specifiedInterfaces.length?==?0)?{Class<?>?targetClass?=?advised.getTargetClass();if?(targetClass?!=?null)?{if?(targetClass.isInterface())?{advised.setInterfaces(targetClass);}else?if?(Proxy.isProxyClass(targetClass))?{advised.setInterfaces(targetClass.getInterfaces());}specifiedInterfaces?=?advised.getProxiedInterfaces();}}//?第三步:為代理類添加三個默認需要實現的接口,分別是//?1.SpringProxy,一個標記接口,代表這個類是通過Spring的AOP代理生成的//?2.Advised,提供了管理通知的方法//?3.DecoratingProxy,用戶獲取到真實的目標對象//?這個真實對象指的是在嵌套代理的情況下會獲取到最終的目標對象//?而不是指返回這個ProxyFactory的targetboolean?addSpringProxy?=?!advised.isInterfaceProxied(SpringProxy.class);boolean?addAdvised?=?!advised.isOpaque()?&&?!advised.isInterfaceProxied(Advised.class);boolean?addDecoratingProxy?=?(decoratingProxy?&&?!advised.isInterfaceProxied(DecoratingProxy.class));int?nonUserIfcCount?=?0;if?(addSpringProxy)?{nonUserIfcCount++;}if?(addAdvised)?{nonUserIfcCount++;}if?(addDecoratingProxy)?{nonUserIfcCount++;}Class<?>[]?proxiedInterfaces?=?new?Class<?>[specifiedInterfaces.length?+?nonUserIfcCount];System.arraycopy(specifiedInterfaces,?0,?proxiedInterfaces,?0,?specifiedInterfaces.length);int?index?=?specifiedInterfaces.length;if?(addSpringProxy)?{proxiedInterfaces[index]?=?SpringProxy.class;index++;}if?(addAdvised)?{proxiedInterfaces[index]?=?Advised.class;index++;}if?(addDecoratingProxy)?{proxiedInterfaces[index]?=?DecoratingProxy.class;}return?proxiedInterfaces; }invoke方法分析
在確認了需要實現的接口后,直接調用了jdk的動態代理方法,這個我們就不做分析了,接下來我們來看看Spring是如何將通知應用到代理對象上的,對應的要分析的代碼就是JdkDynamicAopProxy的invoke方法,源碼如下:
//?這個方法的代碼稍微有點長,代碼也比較難,希望大家能耐心看完 public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{Object?oldProxy?=?null;boolean?setProxyContext?=?false;TargetSource?targetSource?=?this.advised.targetSource;Object?target?=?null;try?{//?首先處理的是hashCode跟equals方法//?如果接口中沒有定義這兩個方法,那么會調用本類中定義的equals方法//?前面我們也說過了,只有當兩個類的目標對象,通知以及實現的接口都相等的情況下//?equals才會返回true//?如果接口中定義了這兩個方法,那么最終會調用目標對象中的方法if?(!this.equalsDefined?&&?AopUtils.isEqualsMethod(method))?{return?equals(args[0]);}else?if?(!this.hashCodeDefined?&&?AopUtils.isHashCodeMethod(method))?{return?hashCode();}//?也就是說我們調用的是DecoratingProxy這個接口中的方法//?這個接口中只定義了一個getDecoratedClass方法,用于獲取到//?最終的目標對象,在方法實現中會通過一個while循環來不斷接近//?最終的目標對象,直到得到的目標對象不是一個被代理的對象才會返回else?if?(method.getDeclaringClass()?==?DecoratingProxy.class)?{return?AopProxyUtils.ultimateTargetClass(this.advised);}//?說明調用的是Advised接口中的方法,這里只是單純的進行反射調用else?if?(!this.advised.opaque?&&?method.getDeclaringClass().isInterface()?&&method.getDeclaringClass().isAssignableFrom(Advised.class))?{return?AopUtils.invokeJoinpointUsingReflection(this.advised,?method,?args);}Object?retVal;//?說明需要將代理類暴露到線程上下文中//?調用AopContext.setCurrentProxy方法將其放入到一個threadLocal中if?(this.advised.exposeProxy)?{oldProxy?=?AopContext.setCurrentProxy(proxy);setProxyContext?=?true;}//?接下來就是真正的執行代理邏輯了target?=?targetSource.getTarget();Class<?>?targetClass?=?(target?!=?null???target.getClass()?:?null);//?先獲取整個攔截器鏈List<Object>?chain?=?this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,?targetClass);//?如果沒有進行攔截,直接反射調用方法if?(chain.isEmpty())?{Object[]?argsToUse?=?AopProxyUtils.adaptArgumentsIfNecessary(method,?args);retVal?=?AopUtils.invokeJoinpointUsingReflection(target,?method,?argsToUse);}//?否則開始執行整個鏈條else?{MethodInvocation?invocation?=new?ReflectiveMethodInvocation(proxy,?target,?method,?args,?targetClass,?chain);retVal?=?invocation.proceed();}//?這里是處理一種特殊情況,就是當執行的方法返回值為this的情況//?這種情況下,需要返回當前的代理對象而不是目標對象Class<?>?returnType?=?method.getReturnType();if?(retVal?!=?null?&&?retVal?==?target?&&returnType?!=?Object.class?&&?returnType.isInstance(proxy)?&&!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass()))?{retVal?=?proxy;}else?if?(retVal?==?null?&&?returnType?!=?Void.TYPE?&&?returnType.isPrimitive())?{throw?new?AopInvocationException("Null?return?value?from?advice?does?not?match?primitive?return?type?for:?"?+?method);}return?retVal;}finally?{if?(target?!=?null?&&?!targetSource.isStatic())?{targetSource.releaseTarget(target);}if?(setProxyContext)?{AopContext.setCurrentProxy(oldProxy);}} }在上面整個流程中,我們抓住核心的兩步
獲取整個攔截器鏈
開始在攔截器鏈上執行方法
我們先看第一步,對應源碼如下:
public?List<Object>?getInterceptorsAndDynamicInterceptionAdvice(Method?method,?@Nullable?Class<?>?targetClass)?{MethodCacheKey?cacheKey?=?new?MethodCacheKey(method);List<Object>?cached?=?this.methodCache.get(cacheKey);if?(cached?==?null)?{//?調用了advisorChainFactory的getInterceptorsAndDynamicInterceptionAdvice方法cached?=?this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this,?method,?targetClass);this.methodCache.put(cacheKey,?cached);}return?cached; } public?List<Object>?getInterceptorsAndDynamicInterceptionAdvice(Advised?config,?Method?method,?@Nullable?Class<?>?targetClass)?{List<Object>?interceptorList?=?new?ArrayList<Object>(config.getAdvisors().length);Class<?>?actualClass?=?(targetClass?!=?null???targetClass?:?method.getDeclaringClass());//?是否有引入通知boolean?hasIntroductions?=?hasMatchingIntroductions(config,?actualClass);AdvisorAdapterRegistry?registry?=?GlobalAdvisorAdapterRegistry.getInstance();//?獲取到所有的通知for?(Advisor?advisor?:?config.getAdvisors())?{//?除了引入通知外,可以認為所有的通知都是一個PointcutAdvisorif?(advisor?instanceof?PointcutAdvisor)?{PointcutAdvisor?pointcutAdvisor?=?(PointcutAdvisor)?advisor;// config.isPreFiltered:代表的是配置已經過濾好了,是可以直接應用的//?這句代碼的含義就是配置是預過濾的或者在類級別上是匹配的if?(config.isPreFiltered()?||?pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass))?{//?接下來要判斷在方法級別上是否匹配MethodMatcher?mm?=?pointcutAdvisor.getPointcut().getMethodMatcher();if?(MethodMatchers.matches(mm,?method,?actualClass,?hasIntroductions))?{//?將通知轉換成對應的攔截器//?有些通知本身就是攔截器,例如環繞通知//?有些通知需要通過一個AdvisorAdapter來適配成對應的攔截器//?例如前置通知,后置通知,異常通知等//?其中MethodBeforeAdvice會被適配成MethodBeforeAdviceInterceptor//?AfterReturningAdvice會被適配成AfterReturningAdviceInterceptor//?ThrowAdvice會被適配成ThrowsAdviceInterceptorMethodInterceptor[]?interceptors?=?registry.getInterceptors(advisor);//?如果是動態的攔截,會創建一個InterceptorAndDynamicMethodMatcher//?動態的攔截意味著需要根據具體的參數來決定是否進行攔截if?(mm.isRuntime())?{for?(MethodInterceptor?interceptor?:?interceptors)?{interceptorList.add(new?InterceptorAndDynamicMethodMatcher(interceptor,?mm));}}else?{interceptorList.addAll(Arrays.asList(interceptors));}}}}else?if?(advisor?instanceof?IntroductionAdvisor)?{//?說明是引入通知IntroductionAdvisor?ia?=?(IntroductionAdvisor)?advisor;if?(config.isPreFiltered()?||?ia.getClassFilter().matches(actualClass))?{//?前文我們有提到過,引入通知實際就是通過一個攔截器//?將方法交由引入的類執行而不是目標類Interceptor[]?interceptors?=?registry.getInterceptors(advisor);interceptorList.addAll(Arrays.asList(interceptors));}}else?{//?可能會擴展出一些通知,一般不會Interceptor[]?interceptors?=?registry.getInterceptors(advisor);interceptorList.addAll(Arrays.asList(interceptors));}}return?interceptorList; }在構建好攔截器鏈后,接下來就是真正執行方法了,對應代碼就是
//?先創建一個MethodInvocation MethodInvocation?invocation?=new?ReflectiveMethodInvocation(proxy,?target,?method,?args,?targetClass,?chain); //?開始在攔截器鏈上執行這個方法 retVal?=?invocation.proceed();最后的關鍵代碼就落在了ReflectiveMethodInvocation的proceed方法
public?Object?proceed()?throws?Throwable?{//?滿足這個條件,說明執行到了最后一個攔截器,那么直接反射調用目標方法if?(this.currentInterceptorIndex?==?this.interceptorsAndDynamicMethodMatchers.size()?-?1)?{return?invokeJoinpoint();}//?獲取到下一個要執行的攔截器Object?interceptorOrInterceptionAdvice?=this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);if?(interceptorOrInterceptionAdvice?instanceof?InterceptorAndDynamicMethodMatcher)?{//?前面構建攔截器鏈的時候我們可以看到,動態的攔截的話會創建一個InterceptorAndDynamicMethodMatcherInterceptorAndDynamicMethodMatcher?dm?=(InterceptorAndDynamicMethodMatcher)?interceptorOrInterceptionAdvice;if?(dm.methodMatcher.matches(this.method,?this.targetClass,?this.arguments))?{return?dm.interceptor.invoke(this);}else?{//?如果匹配失敗了,執行攔截器鏈中的下一個攔截邏輯return?proceed();}}else?{//?調用攔截器中的invoke方法,可以看到這里將this作為參數傳入了//?所以我們在攔截器中調用?MethodInvocation的proceed時又會進行入當前這個方法//?然后去執行鏈條中的下一個攔截器?return?((MethodInterceptor)?interceptorOrInterceptionAdvice).invoke(this);} }總結
本文主要是為下篇文章做準備,下篇文章將會結束整個IOC流程的分析,IOC的最后一步便是為Bean創建代理。本文已經分析了代理的具體創建邏輯,在下篇文章中我們主要結合Spring的啟動流程來看一看Spring是如何將通知添加到創建代理的配置信息中去的。
關于整個IOC跟AOP的模塊還會有兩篇文章,一篇用于結束整個IOC流程,另外一篇專門探討Spring中循環依賴的解決。完成這兩篇文章中,接下來打算用5到7篇文章對Spring的事務管理進行分析!
如果我的文章能幫到你,記得點個贊哈~!
總結
以上是生活随笔為你收集整理的Spring中AOP相关的API及源码解析,原来AOP是这样子的的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 糟糕!HttpClient 连接池设置引
- 下一篇: 终于弄明白 i = i++和 i = +