前言
往期文章:
在上一章向您生動(dòng)地講解Spring AOP 源碼(2)中,作者介紹了【如何獲取對(duì)應(yīng) Bean 適配的Advisors 鏈】娄涩。
在本章中寂拆,作者會(huì)向您介紹埠况,Spring AOP 是如何解析我們配置的Aspect,并將advice織入的兄一?
在本章的附錄部分,還會(huì)介紹如何保存 JDK動(dòng)態(tài)代理和 Cglib生成的類文件。
閑話不多說捷兰,讓我們直接開始。
創(chuàng)建代理類
上一章結(jié)束之后负敏,Spring AOP的核心邏輯已經(jīng)走了一半了贡茅,獲取了目標(biāo)類所適用的增強(qiáng)器列表,下面開始分析獲取代理的過程其做。
未免讀者閱讀不連貫顶考,我們?cè)儋N一下向您生動(dòng)地講解Spring AOP 源碼(1)中我們最后講解的一段源碼,由此繼續(xù)往下講述妖泄。
源碼位置:AbstractAutoProxyCreator#wrapIfNecessary(..)
TODO-2
createProxy
稍微提一下 TargetSource
這個(gè)概念驹沿,它用于封裝真實(shí)實(shí)現(xiàn)類的信息,在我理解看來就是把獲取目標(biāo)對(duì)象這個(gè)步驟做了一個(gè)代理的操作蹈胡,提供一個(gè)擴(kuò)展點(diǎn)給外部渊季,使得使用者可以通過這個(gè)擴(kuò)展點(diǎn)去對(duì)目標(biāo)對(duì)象做一些處理;
上面用了 SingletonTargetSource
這個(gè)實(shí)現(xiàn)類罚渐,其實(shí)我們這里也不太需要關(guān)心這個(gè)却汉,知道有這么回事就可以了,個(gè)人感覺這個(gè)擴(kuò)展點(diǎn)用處不是特別的大荷并。
來?xiàng)l分割線合砂,正式進(jìn)入今天的核心內(nèi)容。
現(xiàn)在源织,讓我們開始解析翩伪,Spring AOP創(chuàng)建代理類的流程。
源碼位置:AbstractAutoProxyCreator#createProxy(..)
流程:
- 獲取當(dāng)前類中的屬性
- 添加代理接口
- 封裝Advisor并加入到ProxyFactory中
- 設(shè)置要代理的類
- Spring中為子類提供了定制的函數(shù)
customizeProxyFactory
谈息,子類可以在此函數(shù)中對(duì)ProxyFactory的進(jìn)一步封裝 - ★★★ 獲取代理操作
主要分析關(guān)鍵的生成代理類的操作缘屹。
源碼位置:ProxyFactory#getProxy(..)
public Object getProxy(ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
這里要分為兩步,
- 創(chuàng)建
AopProxy
- 獲取代理類
1. 創(chuàng)建 AopProxy
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
return getAopProxyFactory().createAopProxy(this);
}
這一步之后我們根據(jù)ProxyConfig 獲取到了對(duì)應(yīng)的AopProxy
的實(shí)現(xiàn)類黎茎,分別是JdkDynamicAopProxy
和ObjenesisCglibAopProxy
囊颅。
2. 獲取代理類
JDK 動(dòng)態(tài)代理
源碼位置:JdkDynamicAopProxy#getProxy(..)
我們關(guān)注的是最后一行代碼Proxy.newProxyInstance(classLoader, proxiedInterfaces, this)
,
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) {
...
}
注:到這里傅瞻,你需要了解一下JDK動(dòng)態(tài)代理的使用知識(shí)踢代,如果能了解原理,那就更好了
第一個(gè)參數(shù)是類加載器嗅骄,第二個(gè)參數(shù)是目標(biāo)類的接口集合胳挎,第三個(gè)參數(shù)則是InvocationHandler
的實(shí)現(xiàn)類,JdkDynamicAopProxy
在創(chuàng)建代理的時(shí)候溺森,是將自身作為 InvocationHandler
傳入的慕爬,由此可知JdkDynamicAopProxy
本身實(shí)現(xiàn)了InvocationHandler
接口窑眯。
熟悉JDK動(dòng)態(tài)代理實(shí)現(xiàn)機(jī)制的同學(xué)應(yīng)該會(huì)知道,調(diào)用代理類的對(duì)應(yīng)方法時(shí)医窿,代理類實(shí)際上是通過invoke(Object proxy, Method method, Object[] args)
方法來完成 target class 方法的調(diào)用磅甩,并在里面進(jìn)行一些代理類想做的其他的操作。
在AOP中姥卢,invoke
方法會(huì)完成AOP編織實(shí)現(xiàn)的封裝卷要。所以讓我們看看這個(gè)invoke
方法是怎么實(shí)現(xiàn)的。
invoke
方法的關(guān)鍵就在于独榴,利用責(zé)任鏈模式僧叉,遞歸調(diào)用的方法,來完成advice 的織入棺榔。
ReflectiveMethodInvocation
構(gòu)造方法
關(guān)鍵的ReflectiveMethodInvocation#proceed()
方法
我們來看看((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this)
瓶堕,這個(gè)方法有多種實(shí)現(xiàn),其中一些我們熟悉的或者說需要關(guān)注的實(shí)現(xiàn)症歇,對(duì)應(yīng)的就是我們Advice的類型郎笆,或者說增強(qiáng)的時(shí)機(jī)。
術(shù)語(yǔ) | 概念 |
---|---|
Before |
在方法被調(diào)用之前執(zhí)行增強(qiáng) |
After |
在方法被調(diào)用之后執(zhí)行增強(qiáng) |
After-returning |
在方法成功執(zhí)行之后執(zhí)行增強(qiáng) |
After-throwing |
在方法拋出指定異常后執(zhí)行增強(qiáng) |
Around |
在方法調(diào)用的前后執(zhí)行自定義的增強(qiáng)行為(最靈活的方式) |
這里我們用概覽的方式過一下這幾種的實(shí)現(xiàn)忘晤,
① MethodBeforeAdviceInterceptor#invoke(..)
② AspectJAfterAdvice#invoke(..)
③ AfterReturningAdviceInterceptor#invoke(..)
④ AspectJAfterThrowingAdvice#invoke(..)
⑤ AspectJAroundAdvice#invoke(..)
Cglib 代理
Cglib 代理 和 JDK 代理 在流程上相似题画,只是在具體實(shí)現(xiàn)上不一樣。核心就是Enhancer
和獲得callbacks
的過程德频。這里就不分析了。
小結(jié)
本章的核心內(nèi)容就是缩幸,創(chuàng)建代理類時(shí)壹置,Spring 根據(jù) AOP 配置選擇JDK動(dòng)態(tài)代理或是 Cglib 代理,增強(qiáng)器的織入是按照事先排序好的順序表谊、advice 的類型來起作用的钞护。
個(gè)人認(rèn)為核心難點(diǎn)還是在對(duì)JDK動(dòng)態(tài)代理和Cglib代理 原理的理解。讀者如果對(duì)這塊不熟悉爆办,可以查閱其他的文章進(jìn)行學(xué)習(xí)难咕。
可以學(xué)習(xí)到責(zé)任鏈的設(shè)計(jì)模式、JDK 動(dòng)態(tài)代理和反射距辆、Cglib代理等Java 核心知識(shí)余佃。
最后,作者寫到這里跨算,也是長(zhǎng)呼一口氣爆土,源碼分析不像新技術(shù)那樣,一開始就抓人眼球诸蚕,很難寫得引人入勝步势,通常篇幅過長(zhǎng)氧猬,寫的人會(huì)乏,看的人也會(huì)乏坏瘩。所幸作者堅(jiān)持了下來盅抚,在這期間對(duì)AOP的源碼也有了更深的理解。
附錄
理解JDK 動(dòng)態(tài)代理 和 CGLIB 代理 生成的代理類的源碼會(huì)讓你對(duì)advice織入的時(shí)機(jī)有更深的理解倔矾。
JDK 動(dòng)態(tài)代理 類源碼
TestSvc
public interface TestSvc {
void process();
}
@Service("testSvc")
public class TestSvcImpl implements TestSvc {
@Override
public void process() {
System.out.println("test svc is working");
}
}
生成代理類:
關(guān)鍵點(diǎn):實(shí)現(xiàn)接口妄均,method.invoke(..) 反射調(diào)用
package com.sun.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import ric.study.demo.aop.svc.TestSvc;
public final class $Proxy19
extends Proxy
implements TestSvc
{
private static Method m1;
private static Method m2;
private static Method m0;
private static Method m3;
public $Proxy19(InvocationHandler paramInvocationHandler)
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void process()
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m3 = Class.forName("ric.study.demo.aop.svc.TestSvc").getMethod("process", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
如何保存JDK 動(dòng)態(tài)代理的源文件
只需要在系統(tǒng)變量中設(shè)置sun.misc.ProxyGenerator.saveGeneratedFiles
為true
即可。比如這樣破讨,
會(huì)在項(xiàng)目目錄下生成com.sun.proxy
目錄丛晦,并存儲(chǔ)對(duì)應(yīng)的文件。想要找到你的代理類到底是哪個(gè)提陶,你還需要打印出(或者debug查看)這個(gè)代理類的類名烫沙,像我上圖一樣。
Cglib 代理 類源碼
關(guān)鍵:繼承隙笆;MethodInterceptor.intercept()锌蓄;
BTW:Cglib 的 源碼未免太過冗長(zhǎng),放上來的閱讀體驗(yàn)非常不好(1000+行)撑柔。讀者可以按照我后面提到的方法自己生成瘸爽,然后利用反編譯工具查看。
如何保存 Cglib 生成代理類 的源文件
和JDK 動(dòng)態(tài)代理類似铅忿,System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "...");
剪决,設(shè)置class 文件的輸出目錄即可。
如果本文有幫助到你檀训,希望能點(diǎn)個(gè)贊柑潦,這是對(duì)我的最大動(dòng)力。