Spring AOP實現原理(遞歸攔截器)


一、AOP(Aspect Orient Programming,面向切面編程)相關術語:

1. 切面(Aspect):實現通用問題的類,例如日志,事務管理,定義了切入點和通知的類,通知和切入點共同組成了切面:時間、地點、做什么

2. 通知(Advice):類似Spring攔截器或者Servlet過濾器,是方法,定義切面要做什么,何時使用,有before,after,around..

3. 連接點(Joinpoint): 程序能夠使用通知(Advice)的一個時機,即程序執行過程中明確的點,一般是方法的調用,或異常被拋出

4. 切入點(Pointcut): 定義切面發生在哪里,帶了通知的連接點,例如某個類或方法的名稱,在程序中主要體現為切入點表達式

 

5. 引入(Introduction): 向現有的類添加新的屬性或方法

6. 目標(Target): 被通知的對象

7. 代理(Proxy):AOP框架創建的對象,代理就是目標對象的增強,Advice + Target = Proxy

 

8. 織入(Weaving):把切面應用到目標對象時,創建新的代理對象的過程

a. 編譯時織入:AspectJ,靜態AOP,生成的字節碼融入了增強后的AOP對象,性能更好

b. 加載時織入: Instrument

c. 運行時織入:Spring AOP動態代理,在內存中臨時生成一個AOP對象,並在特定的切點做增強處理,僅支持方法級別的切點

 

二、Spring實現AOP的方式

1. 經典的基於代理的AOP:通過在xml文件配置進行代理

2. 自動代理的AOP:在配置文件中,切點跟通知自動匹配

3. AOP標簽配置到切面

4. @AspectJ注解

 

三、支持的通知類型:Before、After-returning、After-throwing、Around、Introduction

 

四、Spring AOP工廠

1. 如果AdvisedSupport通知器沒有進行自定義設置,默認使用JDK動態代理

2. 如果AdvisedSupport進行了設置,就判斷要代理的類是不是接口,接口使用JDK動態代理,否則使用Cglib動態代理

3. JDK動態代理和Cglib動態代理,都是基於 AdvisedSupport 的 MethodInterceptor 鏈實現的

    @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()) {
                return new JdkDynamicAopProxy(config);
            }
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }

 

五、基於JDK動態代理實現的AOP,要去實現InvocationHandler接口,調用invoke()方法

1. Spring會根據AdvisedSupport獲取一個方法攔截器MethodInterceptor的鏈條chain

2. 如果chain為空,直接反射調用原方法

3. 否則,調用proceed()方法,而此方法是一個遞歸方法

@Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MethodInvocation invocation;
        Object oldProxy = null;
        boolean setProxyContext = false;

        TargetSource targetSource = this.advised.targetSource;
        Class<?> targetClass = null;
        Object target = null;

        try {
            if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
                // The target does not implement the equals(Object) method itself.
                return equals(args[0]);
            }
            if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
                // The target does not implement the hashCode() method itself.
                return hashCode();
            }
            if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                    method.getDeclaringClass().isAssignableFrom(Advised.class)) {
                // Service invocations on ProxyConfig with the proxy config...
                return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
            }

            Object retVal;

            if (this.advised.exposeProxy) {
                // Make invocation available if necessary.
                oldProxy = AopContext.setCurrentProxy(proxy);
                setProxyContext = true;
            }

            // May be null. Get as late as possible to minimize the time we "own" the target,
            // in case it comes from a pool.
            target = targetSource.getTarget();
            if (target != null) {
                targetClass = target.getClass();
            }

            // Get the interception chain for this method.
            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

            // Check whether we have any advice. If we don't, we can fallback on direct
            // reflective invocation of the target, and avoid creating a MethodInvocation.
            if (chain.isEmpty()) {
                // We can skip creating a MethodInvocation: just invoke the target directly
                // Note that the final invoker must be an InvokerInterceptor so we know it does
                // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
                retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
            }
            else {
                // We need to create a method invocation...
                invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                // Proceed to the joinpoint through the interceptor chain.
                retVal = invocation.proceed();
            }

            // Massage return value if necessary.
            Class<?> returnType = method.getReturnType();
            if (retVal != null && retVal == target && returnType.isInstance(proxy) &&
                    !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
                // Special case: it returned "this" and the return type of the method
                // is type-compatible. Note that we can't help if the target sets
                // a reference to itself in another returned object.
                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()) {
                // Must have come from TargetSource.
                targetSource.releaseTarget(target);
            }
            if (setProxyContext) {
                // Restore old proxy.
                AopContext.setCurrentProxy(oldProxy);
            }
        }
    }

 

六、基於Cglib動態代理實現的AOP,也是基於AdvisedSupport實現Callback(),進行方法攔截

private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
        // Parameters used for optimisation choices...
        boolean exposeProxy = this.advised.isExposeProxy();
        boolean isFrozen = this.advised.isFrozen();
        boolean isStatic = this.advised.getTargetSource().isStatic();

        // Choose an "aop" interceptor (used for AOP calls).
        Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);

            ..............
}

 

七、遞歸攔截器鏈

1. 所有攔截器都執行完了,才會調用真正的防范,否則就遞歸調用攔截器

2. Before攔截器,先執行before再執行proceed,是攔截器鏈的的最后一環

3. After攔截器,先執行proceed再執行after

 

八、java.lang.instrument包進行靜態代理實現,LTW加載時織入(Load Time Weaving)

1.  在JVM啟動時會裝配並應用ClassTransformer,對類字節碼進行轉換,進而實現AOP的功能

2. 使用起來比較麻煩,不推薦使用

 

九、AspectJ靜態代理

 

 

 

 

 

 

參考:

https://www.cnblogs.com/kevin-yuan/p/5571200.html

https://blog.csdn.net/u012707422/article/details/93894174

https://blog.csdn.net/u011983531/article/details/80359304

https://www.jianshu.com/p/012f950206ca

https://blog.csdn.net/u011983531/article/details/49391129

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM