Spring AOP 代理建立流程與執行流程
Spring Aop 代理建立方式:https://www.cnblogs.com/jhxxb/p/14097866.html
最後都會走到ProxyCreatorSupport#createAopProxy 中,拿到 AopProxy,然後呼叫 getProxy 方法獲取代理物件
public class ProxyCreatorSupport extends AdvisedSupport { protected final synchronized AopProxy createAopProxy() { if (!this.active) { activate(); }return getAopProxyFactory().createAopProxy(this); }
DefaultAopProxyFactory
createAopProxy 方法,它的唯一實現為 DefaultAopProxyFactory
/** * 預設情況下,實現了介面,就使用 JDK 動態代理,沒有就使用 CGLIB */ public class DefaultAopProxyFactory implements AopProxyFactory, Serializable { @Override public AopProxy createAopProxy(AdvisedSupport config) throwsAopConfigException { 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."); }if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } } /** * Determine whether the supplied {@link AdvisedSupport} has only the {@link org.springframework.aop.SpringProxy} interface specified (or no proxy interfaces specified at all). */ private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) { Class<?>[] ifcs = config.getProxiedInterfaces(); return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0]))); } }
AopProxy 有兩個實現類,通過getProxy 方法建立代理物件
JdkDynamicAopProxy
/** * 實現了 InvocationHandler,所以處理器就是自己。會實現 invoke 方法 * 是 final 類,預設是 package 的訪問許可權 */ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable { /** * 儲存這個 AOP 代理所有的配置資訊,包括所有的增強器等等 */ private final AdvisedSupport advised; // 標記 equals 和 hashCode 方法是否定義在了介面上 private boolean equalsDefined; private boolean hashCodeDefined; public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException { Assert.notNull(config, "AdvisedSupport must not be null"); // 內部再校驗一次:必須有至少一個增強器和目標例項才行 if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) { throw new AopConfigException("No advisors and no TargetSource specified"); } this.advised = config; } @Override public Object getProxy() { return getProxy(ClassUtils.getDefaultClassLoader()); } /** * 真正建立 JDK 動態代理例項的地方 */ @Override public Object getProxy(@Nullable ClassLoader classLoader) { if (logger.isTraceEnabled()) { logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource()); } // 這部很重要,就是去找介面,我們看到最終代理的介面就是這裡返回的所有介面們(除了我們自己的介面,還有 Spring 預設的一些介面)大致過程如下: // 1、獲取目標物件自己實現的介面們(最終肯定都會被代理的) // 2、是否新增 SpringProxy 介面:目標物件實現對就不添加了,沒實現過就新增 true // 3、是否新增 Adviced 介面,注意不是 Advice 通知介面。實現過就不實現了,沒實現過並且 advised.isOpaque()=false 就新增(預設是會新增的) // 4、是否新增 DecoratingProxy 介面(Spring4.3 後才提供)。傳入的引數 decoratingProxy 為 true,並且沒實現過就新增(顯然這裡,首次進來是會新增的) // 5、代理類的介面一共是目標物件的介面加上面三個介面 SpringProxy、Advised、DecoratingProxy(SpringProxy 是個標記介面而已,其餘的介面都有對應的方法的) Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true); findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); // 第三個引數傳的 this,處理器就是自己,到此一個代理物件就此 new 出來了 return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); } /** * 看接口裡有沒有自己定義 equals 和 hashCode方法,這個很重要,然後標記一下 */ private void findDefinedEqualsAndHashCodeMethods(Class<?>[] proxiedInterfaces) { for (Class<?> proxiedInterface : proxiedInterfaces) { Method[] methods = proxiedInterface.getDeclaredMethods(); // 此處用的是 getDeclaredMethods,只會找自己的 for (Method method : methods) { if (AopUtils.isEqualsMethod(method)) { this.equalsDefined = true; } if (AopUtils.isHashCodeMethod(method)) { this.hashCodeDefined = true; } if (this.equalsDefined && this.hashCodeDefined) { // 兩個都找到了就沒必要繼續迴圈 return; } } } } /** * 對於這部分程式碼和採用 CGLIB 的大部分邏輯都是一樣的,Spring 對此的解釋很有意思: * 本來是可以抽取出來的,使得程式碼看起來更優雅。但是因為此會帶來 10% 的效能損耗,所以 Spring 最終採用了貼上複製的方式各用一份 * Spring 說它提供了基礎的套件,來保證兩個的執行行為是一致的。 * proxy:指的是我們所代理的那個真實物件;method:指的是我們所代理的那個真實物件的某個方法的 Method 物件;args:指的是呼叫那個真實物件方法的引數。 */ @Override @Nullable public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; // 進入 invoke 方法後,最終操作的是 targetSource 物件 // 因為 InvocationHandler 持久的就是 targetSource,最終通過 getTarget 拿到目標物件 TargetSource targetSource = this.advised.targetSource; Object target = null; try { // “通常情況” Spring AOP 不會對 equals、hashCode 方法進行攔截增強,所以此處做了處理 // equalsDefined 為 false(表示自己沒有定義過 eequals 方法),那就交給代理去比較 // hashCode 同理,只要你自己沒有實現過此方法,那就交給代理 // 需要注意的是:這裡統一指的是,如果介面上有此方法,但是你自己並沒有實現 equals 和 hashCode 方法,那就走 AOP 這裡的實現 // 如果介面上沒有定義此方法,只是實現類裡自己 @Override 了 HashCode,那是無效的,就是普通執行 if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { return equals(args[0]); } else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { return hashCode(); } // DecoratingProxy 的方法和 Advised 介面的方法,都是最終呼叫了 config,也就是 this.advised 去執行的 else if (method.getDeclaringClass() == DecoratingProxy.class) { return AopProxyUtils.ultimateTargetClass(this.advised); } else if (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) { return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args); } Object retVal; // 這個是最終該方法的返回值 // 是否暴露代理物件,預設 false 可配置為 true,如果暴露就意味著允許線上程內共享代理物件, // 注意這是線上程內,也就是說同一執行緒的任意地方都能通過 AopContext 獲取該代理物件,這應該算是比較高階一點的用法了。 if (this.advised.exposeProxy) { // 這裡快取一份代理物件在 oldProxy 裡,後面有用 oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // 通過目標源獲取目標物件(此處 Spring 建議獲取目標物件靠後獲取,而不是放在上面) target = targetSource.getTarget(); Class<?> targetClass = (target != null ? target.getClass() : null); // 獲取作用在這個方法上的所有攔截器鏈,參見 DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice 方法 // 會根據切點表示式去匹配這個方法。因此其實每個方法都會進入這裡,只是有很多方法的 chain 是 Empty 而已 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); if (chain.isEmpty()) { // 若攔截器為空,那就直接呼叫目標方法了 // 對引數進行適配:主要處理一些陣列型別的引數,看是表示一個引數,還是表示多個引數(可變引數最終到此都是陣列型別,所以最好是需要一次適配) Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); // 直接呼叫目標方法 retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } else { // 建立一個 invocation ,此處為 ReflectiveMethodInvocation 最終是通過它,去執行前置加強、後置加強等等邏輯 MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // 此處會執行所有的攔截器鏈,交給 AOP 聯盟的 MethodInvocation 去處理。當然實現還是 Spring 的 ReflectiveMethodInvocation retVal = invocation.proceed(); } // 獲取返回值的型別 Class<?> returnType = method.getReturnType(); if (retVal != null && retVal == target && returnType != Object.class && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { // 一些列的判斷條件,如果返回值不為空,且為目標物件的話,就直接將目標物件賦值給 retVal retVal = proxy; } else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) { // 返回 null,且還不是 Void 型別,就拋錯 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) { // 把老的代理物件重新 set 進去 AopContext.setCurrentProxy(oldProxy); } } } /** * AOP 幫我們實現的 equals 方法 */ @Override public boolean equals(@Nullable Object other) { if (other == this) { return true; } if (other == null) { return false; } JdkDynamicAopProxy otherProxy; if (other instanceof JdkDynamicAopProxy) { otherProxy = (JdkDynamicAopProxy) other; } else if (Proxy.isProxyClass(other.getClass())) { InvocationHandler ih = Proxy.getInvocationHandler(other); if (!(ih instanceof JdkDynamicAopProxy)) { return false; } otherProxy = (JdkDynamicAopProxy) ih; } else { return false; } return AopProxyUtils.equalsInProxy(this.advised, otherProxy.advised); } /** * AOP 幫我們實現的 hashCode 方法 */ @Override public int hashCode() { return JdkDynamicAopProxy.class.hashCode() * 13 + this.advised.getTargetSource().hashCode(); } }
除了實現類裡自己寫的方法(介面上沒有),其餘方法統一都會進入代理的 invoke() 方法裡面。只是 invoke 上做了很多特殊處理,比如 DecoratingProxy 和 Advised 等的方法,都是直接執行了。
object 的方法中,toString() 方法會被增強。
ObjenesisCglibAopProxy
/** * 繼承自 CglibAopProxy,它只重寫了 createProxyClassAndInstance 方法 */ class ObjenesisCglibAopProxy extends CglibAopProxy { private static final Log logger = LogFactory.getLog(ObjenesisCglibAopProxy.class); // 另外一種建立例項的方式,可以不用空的建構函式 private static final SpringObjenesis objenesis = new SpringObjenesis(); public ObjenesisCglibAopProxy(AdvisedSupport config) { super(config); } @Override protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) { // 建立一個代理得例項 Class<?> proxyClass = enhancer.createClass(); Object proxyInstance = null; if (objenesis.isWorthTrying()) { // 如果為 true,就採用 objenesis 去 new 一個例項 try { proxyInstance = objenesis.newInstance(proxyClass, enhancer.getUseCache()); } catch (Throwable ex) { logger.debug("Unable to instantiate proxy using Objenesis, falling back to regular proxy construction", ex); } } if (proxyInstance == null) { // 若果還為 null,就再去拿建構函式(指定引數的) try { Constructor<?> ctor = (this.constructorArgs != null ? proxyClass.getDeclaredConstructor(this.constructorArgTypes) : proxyClass.getDeclaredConstructor()); // 通過此建構函式去 new 一個例項 ReflectionUtils.makeAccessible(ctor); proxyInstance = (this.constructorArgs != null ? ctor.newInstance(this.constructorArgs) : ctor.newInstance()); } catch (Throwable ex) { throw new AopConfigException("Unable to instantiate proxy using Objenesis, and regular proxy instantiation via default constructor fails as well", ex); } } ((Factory) proxyInstance).setCallbacks(callbacks); return proxyInstance; } }
父類CglibAopProxy
class CglibAopProxy implements AopProxy, Serializable { /** * 它的兩個 getProxy() 相對來說比較簡單,就是使用 CGLIB 的方式,利用 Enhancer 建立了一個增強的例項 * 這裡面比較複雜的地方在:getCallbacks() 這步是比較繁瑣的 * setCallbackFilter 就是看看哪些方法需要攔截,哪些不需要 */ @Override public Object getProxy() { return getProxy(null); } // CGLIB 重寫的兩個方法 @Override public boolean equals(@Nullable Object other) { return (this == other || (other instanceof CglibAopProxy && AopProxyUtils.equalsInProxy(this.advised, ((CglibAopProxy) other).advised))); } @Override public int hashCode() { return CglibAopProxy.class.hashCode() * 13 + this.advised.getTargetSource().hashCode(); } /** * 最後,所有的被代理的類的所有的方法呼叫,都會進入 DynamicAdvisedInterceptor#intercept 這個方法裡面來(相當於 JDK 動態代理的 invoke 方法) * 它實現了 MethodInterceptor 介面 */ private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable { private final AdvisedSupport advised; public DynamicAdvisedInterceptor(AdvisedSupport advised) { this.advised = advised; } @Override @Nullable public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; Object target = null; // 目標物件源 TargetSource targetSource = this.advised.getTargetSource(); try { if (this.advised.exposeProxy) { oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // 拿到目標物件,這裡就是使用 targetSource 的意義,它提供多個實現類,從而實現了更多的可能性 // 比如:SingletonTargetSource、HotSwappableTargetSource、PrototypeTargetSource、ThreadLocalTargetSource 等 target = targetSource.getTarget(); Class<?> targetClass = (target != null ? target.getClass() : null); // 一樣的,也是拿到和這個方法匹配的所有增強器和通知,和 JDK Proxy 中是一樣的 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); Object retVal; if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) { // 沒有增強器,同時該方法是 public 的,就直接呼叫目標方法(不攔截) Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = methodProxy.invoke(target, argsToUse); } else { // CglibMethodInvocation 這裡採用的是 CglibMethodInvocation,它是 ReflectiveMethodInvocation 的子類,到這裡就和 JDK Proxy 保持一致了 retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed(); } retVal = processReturnType(proxy, target, method, retVal); return retVal; } finally { if (target != null && !targetSource.isStatic()) { targetSource.releaseTarget(target); } if (setProxyContext) { AopContext.setCurrentProxy(oldProxy); } } } @Override public boolean equals(@Nullable Object other) { return (this == other || (other instanceof DynamicAdvisedInterceptor && this.advised.equals(((DynamicAdvisedInterceptor) other).advised))); } @Override public int hashCode() { return this.advised.hashCode(); } } private static class CglibMethodInvocation extends ReflectiveMethodInvocation {
和 JDK 的一樣,Object 的方法,只有 toString() 會被攔截(執行通知)
生成出來的代理物件,Spring 預設都給你實現了介面:SpringProxy、Advised
和 JDK 不同的是,比如 equals 和 hashCode 等方法根本就不會進入 intecept 方法,而是在 getCallbacks() 那裡就給特殊處理掉了
總結
關於 final 方法
- JDK 代理:因為介面的方法不能使用 final 關鍵字,所以編譯器就過不去
- CGLIB 代理:final 修飾某個方法後,不報錯。但也不會攔截了
關於 static 方法
- JDK 代理:static 修飾介面上的方法,要求有 body 體(JDK8 後支援)。但是因為子類不能 @Override了,所以編譯就報錯了
- CGLIB 代理:父類方法用 static 修飾後,子類也是無法進行重寫的。因此不報錯,但也不會攔截了
關於非 public 方法
- JDK 代理:介面中的方法都是 public的,所以對於它不存在這種現象
- CGLIB 代理:記住結論,只有 private 的方法不能被代理(因為子類無法訪問),其餘的訪問許可權級別的,都能夠被正常代理
JdkDynamicAopProxy 入口方法是動態代理的 invoke() 方法,CGLIB 使用的是 DynamicAdvisedInterceptor.intercept() 方法
JdkDynamicAopProxy 使用的 MethodInvocation 是: ReflectiveMethodInvocation,CGLIB 使用的是 CglibMethodInvocation,它倆都是 ProxyMethodInvocation 介面的實現類。並且 CglibMethodInvocation 是繼承自 ReflectiveMethodInvocation 的
CGLib 更適合代理不需要頻繁例項化的類,而 Spring 絕大多數 Bean 都是單例的,因此在 Spring AOP 中推薦使用 CGLib,它的功能更強大些