7、spring核心源碼解析之aop概況流程2

1. 源碼分析aop的切面@Aspect解析

​ 前文分析spring ioc源碼時,瞭解到,spring 解析內部註解,通常通過後置處理器進行解析,上文提到 aop注入的後置處理器AnnotationAwareAspectJAutoProxyCreator。在進行切面解析時,其實存在一些父子後置處理器之間的調用關係。

通過源碼瞭解到,@Aspect解析的起點在spring bean的實例化之前AbstractAutoProxyCreator#postProcessBeforeInstantiation方法中。

該方法主要作用是獲取spring容器中的切面類,同時將切面和通知存在緩存中,調用鏈本人通過debug形式打印出來了。

1590298291362

核心代碼AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors,在該方法中找到所有的通知並返回。

	protected List<Advisor> findCandidateAdvisors() {
		// Add all the Spring advisors found according to superclass rules.
		//根據父類的規則,找到所有的spring 通知
		List<Advisor> advisors = super.findCandidateAdvisors();
		// Build Advisors for all AspectJ aspects in the bean factory.
		//獲取Bean工廠中所有AspectJ切面的通知
		if (this.aspectJAdvisorsBuilder != null) {
			//找到所有的通知,並放入到aspectJAdvisorsBuilder中緩存起來,並返回
			advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
		}
		return advisors;
	}

aspectJAdvisorsBuilder這個spring內部使用的建造者模式中,會緩存所用容器中的切面,切點,通知等信息。以便後期需要代理類動態增強時進行織入。

在這裏插入圖片描述

2. 源碼分析aop使用jdk代理還是cglib動態代理?

通過源碼瞭解到,spring在實例化bean之後的後置處理器中AbstractAutoProxyCreator#postProcessAfterInitialization方法中,判斷是否需要去創建代理對象,找到動態代理創建時需要的通知,並進行代理的創建。

我將本文的重點放在了動態代理的創建,至於如何根據切點找到代理類的通知在此不進行詳細描述,這裏是創建代理類的調用鏈。
在這裏插入圖片描述

	@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);
			}
            //cgilb動態代理
			return new ObjenesisCglibAopProxy(config);
		}
		else {
            //jdk動態代理
			return new JdkDynamicAopProxy(config);
		}
	}

在本步驟中spring aop創建了動態代理的實例,並且作爲bean的實例化對象。那麼spring aop具體是如何創建的呢?

3. spring中jdk動態代理和aop動態代理

jdk動態代理:

原理: 利用反射機制生成一個實現代理接口的實現類,在調用具體方法前調用InvokeHandler來處理。

jdk動態代理關鍵點

1.目標類(被代理類)必須實現接口。

2.自定義InvocationHandler 類實現 InvocationHandler 接口的invoke(),在invoke方法中通過反射完成代理對象的方法調用。在該過程中可以增強我們的目標對象方法調用,即織入我們自定義的邏輯。

關鍵增強的邏輯一般都在 實現該接口的invoke()方法中。

來看看spring的jdk動態代理源碼,分析粒度不大,先簡單介紹

熟悉jdk動態代理的同學就知道,jdk動態代理核心就在handler的invoke方法中,即JdkDynamicAopProxy#invoke,該類實現了InvocationHandler 接口的invoke()方法

通過代碼可以看到動態織入通知的起點執行邏輯,

注意: 本文最後會有一張時許圖詳細描述ReflectiveMethodInvocation().proceed()方法中,通知攔截器鏈的織入過程

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

		TargetSource targetSource = this.advised.targetSource;
		Object target = null;

		try {
			if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
				// The target does not implement the equals(Object) method itself.
				return equals(args[0]);
			}
			else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
				// The target does not implement the hashCode() method itself.
				return hashCode();
			}
			else if (method.getDeclaringClass() == DecoratingProxy.class) {
				// There is only getDecoratedClass() declared -> dispatch to proxy config.
				return AopProxyUtils.ultimateTargetClass(this.advised);
			}
			else 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;
			}

			// 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);

			// 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.
				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
			}
			else {
				// We need to create a method invocation...
				//我們需要創建一個方法調用...
				MethodInvocation invocation =
						new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
				// Proceed to the joinpoint through the interceptor chain.
				//通過攔截器鏈進入連接點,該處邏輯與cglib相似,會執行方法攔截intercep。
				retVal = invocation.proceed();
			}

			// Massage return value if necessary.
			Class<?> returnType = method.getReturnType();
			if (retVal != null && retVal == target &&
					returnType != Object.class && 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動態代理:

原理: 是利用asm,加載代理類的class文件,通過修改其字節碼生成子類,覆蓋父類的方法,來進行處理。(注意 :目標類方法最好不要聲明成final )。

cglib動態代理關鍵點

1.目標類(被代理類)不需要實現接口。

2.通過增強器類Enhancer創建代理對象,設置織入回調的攔截器鏈setCallbackFilter,(前置通知等等都是通過此功能實現)。

3.spring aop中有一個關鍵的內部類,定義了動態通知的攔截CglibAopProxy.DynamicAdvisedInterceptor。

4.spring 的方法攔截鏈中的每個攔截器都實現了MethodInterceptor接口。

Cglib中的攔截器有點多,可以看一下CglibAopProxy的內部類帶有Interceptor的很多。

在這裏插入圖片描述

攔截器Interceptor很多,但是我們的關注點應該放在動態織入通知的攔截器中DynamicAdvisedInterceptor的invoke方法,看該方法中的方法攔截,其中和jdk動態代理織入的邏輯相似。都調用了new CglibMethodInvocation().proceed()方法。那麼通知織入的核心其實在該該類的proceed方法中。

@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) {
					// 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...
					//我們需要創建一個方法調用。該方法調用的proceed,就是一個通知動態織入到代理對象的過程
					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);
				}
			}
		}

那就看一下ReflectiveMethodInvocation#proceed是如何執行的

	public Object proceed() throws Throwable {
		// We start with an index of -1 and increment early.
		//我們從索引-1開始並提前增加,直至將所有同通知攔截執行完畢。
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			return invokeJoinpoint();
		}
		//獲取通知攔截器
		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
			// Evaluate dynamic method matcher here: static part will already have
			// been evaluated and found to match.
			//匹配動態通知攔截
			InterceptorAndDynamicMethodMatcher dm =
					(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
			Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
			if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
				//執行通知攔截
				return dm.interceptor.invoke(this);
			}
			else {
				// Dynamic matching failed.
				// Skip this interceptor and invoke the next in the chain.
				//動態匹配失敗,跳過這個攔截器,調用攔截器鏈中下一個攔截器
				return proceed();
			}
		}
		else {
			// It's an interceptor, so we just invoke it: The pointcut will have
			// been evaluated statically before this object was constructed.
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}

將斷點調製該處,我們可以看到通知攔截器鏈的執行順序:

afteThrowingr -> afterReturning -> after ->afterAround -> MethodBefore.

至於爲什麼是這個執行順序,肯定是有講究的,但是本人並沒有繼續深入,有興趣可以繼續追蹤一下源碼。

在這裏插入圖片描述

ok,那麼它是如何執行通知的鏈式調用的呢,代碼看了,看一張時序圖,思路會更加清晰。

4. Advice通知MethodIntercept方法攔截織入代理時序圖

在這裏插入圖片描述

個人覺得,spring的aop實現整體流程涉及到的點很多,但是大體思路並不算很複雜。真正的基礎和複雜點是jdk和cgilib如何在jvm運行時動態生成代理類。

上一篇:6、spring核心源碼解析之aop概況流程1
[下一篇:8.未完待續]

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