Spring AOP動態代理實現源碼解析

寫在AOP之前

一、靜態代理和動態代理

靜態代理:在編譯期生成.class字節碼文件

動態代理:在運行時運用反射機制動態創建

1)靜態代理:

目標對象和代理對象需要實現相同的接口

代理對象需要持有目標對象(一般是目標對象實現的接口)

2)動態代理

JDK動態代理:

Proxy.NewProxyInstance()

InvocationHandler接口

CGLIB動態代理:

實現MethodInterceptor接口,重寫intercept方法

Spring AOP源碼解析

入口:

org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions

org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseCustomElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition)

至此,註冊了AnnotationAwareAspectJAutoProxyCreator到容器中。

可以看到,AnnotationAwareAspectJAutoProxyCreator是一個BeanPostProcessor後置處理器,並且實現了Ordered接口

它的父類AbstractAutoProxyCreator重寫了postProcessBeforeInitialization和postProcessAfterInitialization方法

隨後容器會自動註冊這個BeanPostProcessor。

接下來,在初始化Bean的時候,會調用這個BeanPostProcessor的初始化後處理

然後進到AbstractBeanFactory的getBean()方法,最終進到Bean的初始化方法

在這裏進行BeanPostProcessor後置處理

調用AbstractAutoProxyCreator的postProcessAfterInitialization方法

獲取所有的Advisor

遍歷,找出@Aspect註解的Bean

獲取以上Bean裏的所有Advisor,方法和Advisor一對一

方法1獲取類裏所有的沒有@Pointcut註解的Method(可能包含普通的方法),可能是以下幾種:

  1. @Before
  2. @Aroud
  3. @After
  4. @AfterReturing
  5. @AfterThrowing

方法2獲取以上通知方法的expressionPointcut 切點表達式(普通方法會被過濾掉)

方法getPointcut():

初始化一個增強器

根據不同的通知類型,創建不同的增強器

遍歷所有的Advisors,是否有任意的Advisors的切點matches(當前的bean)。篩選出所有matches的Advisor

注:此處是動態代理的核心!!!

方法1:檢查目標對象是否實現了接口,如果是則調用proxyFactory.addInterface(ifc);方法,這樣在後面的判斷hasNoUserSuppliedProxyInterfaces(config)時就爲false

如果否則設置proxyTargetClass屬性爲true(只有在proxyTargetClass=false的情況下才會進到此方法)

方法2:生成代理對象

動態代理選擇的判斷邏輯:

平時我們說AOP原理三句話就能概括:

1.對類生成代理使用CGLIB

2.對接口生成代理使用JDK原生的Proxy

3.可以通過配置文件指定對接口使用CGLIB生成代理(配置proxy-target-class=true)

這三句話的出處就是createAopProxy方法。

默認使用JDK自帶的Proxy生成代理,碰到以下三種情況例外:

1.ProxyConfig的isOptimize方法爲true,這表示讓Spring自己去優化而不是用戶指定

2.ProxyConfig的isProxyTargetClass方法爲true,這表示配置了proxy-target-class="true"

3.ProxyConfig滿足hasNoUserSuppliedProxyInterfaces方法執行結果爲true,這表示<bean>對象沒有實現任何接口或者實現的接口是SpringProxy接口

注:SpringBoot的AOP是自動開啓的(spring.aop.auto = true)

可配置spring.aop.proxy-target-class = true強制使用CGLIB動態代理

接下來創建代理對象

Proxy.newProxyInstance()方法是執行的關鍵,有三個參數:

ClassLoader loader,

Class<?>[] interfaces,

InvocationHandler h

底層實際是調用InvocationHandler.invoke()方法,在這裏,也就是調用JdkDynamicAopProxy.invoke()方法。

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 {

        // 省略部分代碼

        Object retVal;

        // 如果 expose-proxy 屬性爲 true,則暴露代理對象

        if (this.advised.exposeProxy) {

            // 向 AopContext 中設置代理對象

            oldProxy = AopContext.setCurrentProxy(proxy);

            setProxyContext = true;

        }

        // 獲取適合當前方法的攔截器

        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 = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);

            // 執行攔截器鏈

            retVal = invocation.proceed();

        }

        // 獲取方法返回值類型

        Class<?> returnType = method.getReturnType();

        if (retVal != null && retVal == target &&

                returnType != Object.class && returnType.isInstance(proxy) &&

                !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {

            // 如果方法返回值爲 this,即 return this; 則將代理對象 proxy 賦值給 retVal

            retVal = proxy;

        }

        // 如果返回值類型爲基礎類型,比如 int,long 等,當返回值爲 null,拋出異常

        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()) {

            targetSource.releaseTarget(target);

        }

        if (setProxyContext) {

            AopContext.setCurrentProxy(oldProxy);

        }

    }

}

invoke方法主要邏輯如下:

1. 檢測 expose-proxy 是否爲 true,若爲 true,則暴露代理對象

2. 獲取適合當前方法的攔截器鏈

3. 如果攔截器鏈爲空,則直接通過反射執行目標方法

4. 若攔截器鏈不爲空,則創建方法調用器 ReflectiveMethodInvocation 對象

5. 調用 ReflectiveMethodInvocation 對象的 proceed() 方法啓動攔截器鏈

6. 處理返回值,並返回該值

看下以上的步驟2的代碼:

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {

    MethodCacheKey cacheKey = new MethodCacheKey(method);

    // 從緩存中獲取

    List<Object> cached = this.methodCache.get(cacheKey);

    // 緩存未命中,則進行下一步處理

    if (cached == null) {

        // 獲取所有的攔截器

        cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(

                this, method, targetClass);

        // 存入緩存

        this.methodCache.put(cacheKey, cached);

    }

    return cached;

}

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(

        Advised config, Method method, Class<?> targetClass) {

    List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);

    Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());

    boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);

    // registry 爲 DefaultAdvisorAdapterRegistry 類型

    AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();

    // 遍歷通知器列表

    for (Advisor advisor : config.getAdvisors()) {

        if (advisor instanceof PointcutAdvisor) {

            PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;

            /*

             * 調用 ClassFilter 對 bean 類型進行匹配,無法匹配則說明當前通知器

             * 不適合應用在當前 bean 上

             */

            if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {

                // 將 advisor 中的 advice 轉成相應的攔截器

                MethodInterceptor[] interceptors = registry.getInterceptors(advisor);

                MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();

                // 通過方法匹配器對目標方法進行匹配

                if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {

                    // 若 isRuntime 返回 true,則表明 MethodMatcher 要在運行時做一些檢測

                    if (mm.isRuntime()) {

                        for (MethodInterceptor interceptor : interceptors) {

                            interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));

                        }

                    }

                    else {

                        interceptorList.addAll(Arrays.asList(interceptors));

                    }

                }

            }

        }

        else if (advisor instanceof IntroductionAdvisor) {

            IntroductionAdvisor ia = (IntroductionAdvisor) advisor;

            // IntroductionAdvisor 類型的通知器,僅需進行類級別的匹配即可

            if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {

                Interceptor[] interceptors = registry.getInterceptors(advisor);

                interceptorList.addAll(Arrays.asList(interceptors));

            }

        }

        else {

            Interceptor[] interceptors = registry.getInterceptors(advisor);

            interceptorList.addAll(Arrays.asList(interceptors));

        }

    }

    return interceptorList;

}

public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {

    List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);

    Advice advice = advisor.getAdvice();

    /*

     * 若 advice 是 MethodInterceptor 類型的,直接添加到 interceptors 中即可。

     * 比如 AspectJAfterAdvice 就實現了 MethodInterceptor 接口

     */

    if (advice instanceof MethodInterceptor) {

        interceptors.add((MethodInterceptor) advice);

    }

    /*

     * 對於 AspectJMethodBeforeAdvice 等類型的通知,由於沒有實現 MethodInterceptor

     * 接口,所以這裏需要通過適配器進行轉換

     */

    for (AdvisorAdapter adapter : this.adapters) {

        if (adapter.supportsAdvice(advice)) {

            interceptors.add(adapter.getInterceptor(advisor));

        }

    }

    if (interceptors.isEmpty()) {

        throw new UnknownAdviceTypeException(advisor.getAdvice());

    }

    return interceptors.toArray(new MethodInterceptor[interceptors.size()]);

}

以上代碼邏輯總結如下:

1. 從緩存中獲取當前方法的攔截器鏈

2. 若緩存未命中,則調用 getInterceptorsAndDynamicInterceptionAdvice 獲取攔截器鏈

3. 遍歷通知器列表

4. 對於 PointcutAdvisor 類型的通知器,這裏要調用通知器所持有的切點(Pointcut)對類和方法進行匹配,匹配成功說明應向當前方法織入通知邏輯

5. 調用 getInterceptors 方法對非 MethodInterceptor 類型的通知進行轉換(如:@Before @AfterReturning @AfterThrowing)

6. 返回攔截器數組,並在隨後存入緩存中

接下來就是調用 ReflectiveMethodInvocation 對象的 proceed() 方法啓動攔截器鏈

interceptorOrInterceptionAdvice).invoke(this);

這裏會先執行一個默認MethodInterceptor的invoke()方法,即ExposeInvocationInterceptor

然後遞歸調用ReflectiveMethodInvocation.proceed()方法,以@Before前置通知爲例:

此時這裏調用的是MethodBeforeAdviceInterceptor.invoke()方法

先執行前置通知切面邏輯,然後遞歸調用下一個攔截器的invoke()方法

最終,通過反射調用執行織入的通知邏輯

至此,JDK動態代理源碼分析完畢。

注:JDK動態代理字節碼文件分析

測試類:

生成的字節碼文件:

附:不同的Aspect和Advice的執行順序

https://blog.csdn.net/qq_32331073/article/details/80596084

CGLIB動態代理

入口:

主流程:

核心邏輯:

方法執行的時候,實際上是回調的CglibAopProxy.DynamicAdvisedInterceptor.intercept()方法。

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) {
					// Make invocation available if necessary.
					oldProxy = AopContext.setCurrentProxy(proxy);
					setProxyContext = true;
				}
				// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
				target = targetSource.getTarget();
				Class<?> targetClass = (target != null ? target.getClass() : null);
				List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
				Object retVal;
				// Check whether we only have one InvokerInterceptor: that is,
				// no real advice, but just reflective invocation of the target.
				if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
					// 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.
					Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
					retVal = methodProxy.invoke(target, argsToUse);
				}
				else {
					// We need to create a method invocation...
					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) {
					// Restore old proxy.
					AopContext.setCurrentProxy(oldProxy);
				}
			}
		}

邏輯和JDK動態代理基本一致:

1. 檢測 expose-proxy 是否爲 true,若爲 true,則暴露代理對象

2. 獲取適合當前方法的攔截器鏈

3. 如果攔截器鏈爲空並且該方法是public修飾的,則直接通過反射執行目標方法

4. 若攔截器鏈不爲空,則創建方法調用器 CglibMethodInvocation 對象

5. 調用 CglibMethodInvocation 對象的 proceed() 方法啓動攔截器鏈

6. 處理返回值,並返回該值

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章