往期文章:java
在上一章向您生動地講解Spring AOP 源碼(2)中,做者介紹了【如何獲取對應 Bean 適配的Advisors 鏈】。學習
在本章中,做者會向您介紹,Spring AOP 是如何解析咱們配置的Aspect,並將advice織入的?this
在本章的附錄部分,還會介紹如何保存 JDK動態代理和 Cglib生成的類文件。spa
閒話很少說,讓咱們直接開始。
上一章結束以後,Spring AOP的核心邏輯已經走了一半了,獲取了目標類所適用的加強器列表,下面開始分析獲取代理的過程。
未免讀者閱讀不連貫,咱們再貼一下向您生動地講解Spring AOP 源碼(1)中咱們最後講解的一段源碼,由此繼續往下講述。
源碼位置:AbstractAutoProxyCreator#wrapIfNecessary(..)
TODO-2 createProxy
稍微提一下 TargetSource
這個概念,它用於封裝真實實現類的信息,在我理解看來就是把獲取目標對象這個步驟作了一個代理的操做,提供一個擴展點給外部,使得使用者能夠經過這個擴展點去對目標對象作一些處理;
上面用了 SingletonTargetSource
這個實現類,其實咱們這裏也不太須要關心這個,知道有這麼回事就能夠了,我的感受這個擴展點用處不是特別的大。
來條分割線,正式進入今天的核心內容。
如今,讓咱們開始解析,Spring AOP建立代理類的流程。
源碼位置:AbstractAutoProxyCreator#createProxy(..)
流程:
customizeProxyFactory
,子類能夠在此函數中對ProxyFactory的進一步封裝主要分析關鍵的生成代理類的操做。
源碼位置:ProxyFactory#getProxy(..)
public Object getProxy(ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
複製代碼
這裏要分爲兩步,
AopProxy
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
return getAopProxyFactory().createAopProxy(this);
}
複製代碼
這一步以後咱們根據ProxyConfig 獲取到了對應的AopProxy
的實現類,分別是JdkDynamicAopProxy
和ObjenesisCglibAopProxy
。
源碼位置:JdkDynamicAopProxy#getProxy(..)
咱們關注的是最後一行代碼Proxy.newProxyInstance(classLoader, proxiedInterfaces, this)
,
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {
...
}
複製代碼
注:到這裏,你須要瞭解一下JDK動態代理的使用知識,若是能瞭解原理,那就更好了
第一個參數是類加載器,第二個參數是目標類的接口集合,第三個參數則是InvocationHandler
的實現類,JdkDynamicAopProxy
在建立代理的時候,是將自身做爲 InvocationHandler
傳入的,由此可知JdkDynamicAopProxy
自己實現了InvocationHandler
接口。
熟悉JDK動態代理實現機制的同窗應該會知道,調用代理類的對應方法時,代理類其實是經過invoke(Object proxy, Method method, Object[] args)
方法來完成 target class 方法的調用,並在裏面進行一些代理類想作的其餘的操做。
在AOP中,invoke
方法會完成AOP編織實現的封裝。因此讓咱們看看這個invoke
方法是怎麼實現的。
invoke
方法的關鍵就在於,利用責任鏈模式,遞歸調用的方法,來完成advice 的織入。
ReflectiveMethodInvocation
構造方法
關鍵的ReflectiveMethodInvocation#proceed()
方法
咱們來看看((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this)
,這個方法有多種實現,其中一些咱們熟悉的或者說須要關注的實現,對應的就是咱們Advice的類型,或者說加強的時機。
術語 | 概念 |
---|---|
Before |
在方法被調用以前執行加強 |
After |
在方法被調用以後執行加強 |
After-returning |
在方法成功執行以後執行加強 |
After-throwing |
在方法拋出指定異常後執行加強 |
Around |
在方法調用的先後執行自定義的加強行爲(最靈活的方式) |
這裏咱們用概覽的方式過一下這幾種的實現,
① MethodBeforeAdviceInterceptor#invoke(..)
② AspectJAfterAdvice#invoke(..)
③ AfterReturningAdviceInterceptor#invoke(..)
④ AspectJAfterThrowingAdvice#invoke(..)
⑤ AspectJAroundAdvice#invoke(..)
Cglib 代理 和 JDK 代理 在流程上類似,只是在具體實現上不同。核心就是Enhancer
和得到callbacks
的過程。這裏就不分析了。
本章的核心內容就是,建立代理類時,Spring 根據 AOP 配置選擇JDK動態代理或是 Cglib 代理,加強器的織入是按照事先排序好的順序、advice 的類型來起做用的。
我的認爲核心難點仍是在對JDK動態代理和Cglib代理 原理的理解。讀者若是對這塊不熟悉,能夠查閱其餘的文章進行學習。
能夠學習到責任鏈的設計模式、JDK 動態代理和反射、Cglib代理等Java 核心知識。
最後,做者寫到這裏,也是長呼一口氣,源碼分析不像新技術那樣,一開始就抓人眼球,很難寫得引人入勝,一般篇幅過長,寫的人會乏,看的人也會乏。所幸做者堅持了下來,在這期間對AOP的源碼也有了更深的理解。
理解JDK 動態代理 和 CGLIB 代理 生成的代理類的源碼會讓你對advice織入的時機有更深的理解。
TestSvc
public interface TestSvc {
void process();
}
@Service("testSvc")
public class TestSvcImpl implements TestSvc {
@Override
public void process() {
System.out.println("test svc is working");
}
}
複製代碼
生成代理類:
關鍵點:實現接口,method.invoke(..) 反射調用
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());
}
}
}
複製代碼
只須要在系統變量中設置sun.misc.ProxyGenerator.saveGeneratedFiles
爲true
便可。好比這樣,
會在項目目錄下生成com.sun.proxy
目錄,並存儲對應的文件。想要找到你的代理類究竟是哪一個,你還須要打印出(或者debug查看)這個代理類的類名,像我上圖同樣。
關鍵:繼承;MethodInterceptor.intercept();
BTW:Cglib 的 源碼未免太過冗長,放上來的閱讀體驗很是很差(1000+行)。讀者能夠按照我後面提到的方法本身生成,而後利用反編譯工具查看。
和JDK 動態代理相似,System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "...");
,設置class 文件的輸出目錄便可。
若是本文有幫助到你,但願能點個贊,這是對個人最大動力。