1. 程式人生 > 實用技巧 >Spring AOP 代理建立流程與執行流程

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) throws
AopConfigException { 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,它的功能更強大些


https://blog.csdn.net/f641385712/article/details/88952482