1. 程式人生 > 程式設計 >Spring原始碼閱讀-AOP(二)

Spring原始碼閱讀-AOP(二)

前言

在上一篇文章中,我們已經知道springAOP解析配置檔案的過程,在本篇文章我們將繼續為大家解答下面的兩個問題。
複製程式碼
  • 瞭解spring是如何產生的代理物件
  • springAOP產生的代理物件是怎麼執行的。

Spring是如何產生的代理物件

如果有看過之前我寫的Spring-IOC原始碼閱讀就會知道,我們如果要產生一個代理物件就需要在物件初始化之後建立對應的代理物件,而要進行初始化之後的操作就需用用到BeanPostProcessor中的一個方法postProcessAfterInitialization---初始化之後呼叫。
而我們在上一篇文章中也提到了一個類叫AspectJAwareAdvisorAutoProxyCreator,這個類是在解析配置檔案過程中產生的用於建立代理物件,因此我們閱讀的入口,我們找下這個類及其父類中哪個類實現了postProcessAfterInitialization這個方法,就是建立代理物件的入口。
大家可以自己去找找,最終可以找到父類AbstractAutoProxyCreator實現了postProcessAfterInitialization,我們直接進到這個方法中。
複製程式碼
public Object postProcessBeforeInstantiation(Class<?> beanClass,String beanName) throws BeansException {
		Object cacheKey = getCacheKey(beanClass,beanName);

		if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
			if (this.advisedBeans.containsKey(cacheKey)) {
				return
null; } if (isInfrastructureClass(beanClass) || shouldSkip(beanClass,beanName)) { this.advisedBeans.put(cacheKey,Boolean.FALSE); return null; } } // Create proxy here if we have a custom TargetSource. // Suppresses unnecessary default instantiation of the target bean: // The TargetSource will handle target instances in
a custom fashion. TargetSource targetSource = getCustomTargetSource(beanClass,beanName); if (targetSource != null) { if (StringUtils.hasLength(beanName)) { this.targetSourcedBeans.add(beanName); } Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass,beanName,targetSource); Object proxy = createProxy(beanClass,specificInterceptors,targetSource); this.proxyTypes.put(cacheKey,proxy.getClass()); return proxy; } return null; } 複製程式碼

在這個方法中我們就看到了,createProxy這個建立代理物件的方法,我們繼續往下跟

protected Object createProxy(Class<?> beanClass,@Nullable String beanName,@Nullable Object[] specificInterceptors,TargetSource targetSource) {

		if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory,beanClass);
		}

		// 建立代理工廠物件
		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.copyFrom(this);

		if (!proxyFactory.isProxyTargetClass()) {
			// 是否可能使用CGLib代理
			if (shouldProxyTargetClass(beanClass,beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				// 檢視beanClass對應的類是否含有InitializingBean.class/DisposableBean.class/Aware.class介面
				// 無則採用JDK動態代理,有則採用CGLib動態代理
				evaluateProxyInterfaces(beanClass,proxyFactory);
			}
		}
		// 獲得所有關聯的Advisor集合(該分支待補充)
		Advisor[] advisors = buildAdvisors(beanName,specificInterceptors);
		proxyFactory.addAdvisors(advisors);
		proxyFactory.setTargetSource(targetSource);
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}

		// 獲取使用JDK動態代理或者cglib動態代理產生的物件
		return proxyFactory.getProxy(getProxyClassLoader());
	}
複製程式碼

這裡我們只需要關注最後一行程式碼,獲取代理物件,繼續往下面走

public Object getProxy(@Nullable ClassLoader classLoader) {
		return createAopProxy().getProxy(classLoader);
	}
複製程式碼

這裡建立了一個代理物件工廠來建立代理物件,接著一直往下走可以找到DefaultAopProxyFacory中的createAopProxy方法

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.");
			}
			// 如果目標類是介面或者是Proxy的子類,那麼使用JDK動態代理方式生成代理物件
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
			// 使用Cglib動態代理
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			// 預設使用jdk
			return new JdkDynamicAopProxy(config);
		}
	}
複製程式碼

這裡我們就知道了這個工廠其實就是判斷是用jdk還是用cglib生成代理物件。我們就看jdk生成代理物件的過程,這裡會返回一個JdkDynamicAopProxy,用於建立代理物件,我們接著最上面的程式碼getProxy(),找到JdkDynamicAopProxy的getProxy方法

@Override
	public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isDebugEnabled()) {
			logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
		}
		Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised,true);
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
		// 呼叫JDK動態代理方法
		return Proxy.newProxyInstance(classLoader,proxiedInterfaces,this);
	}

複製程式碼

看到這裡,如果對動態代理有些瞭解的同學就應該很清楚了,Proxy.newProxyInstance(classLoader,this);這行程式碼就是jdk建立動態代理物件的api。 這裡需要注意到,這裡建立代理物件中這個InvocationHandler穿的的this。這就意味著,這個類不用來建立代理物件而且用來執行代理物件。裡面肯定有invoke方法。

總結

AOP建立代理物件其實就是AbstractAutoProxyCreator實現了BeanPostProcessor的postProcessAfterInitialization方法,在物件初始化完成之後通過jdk或者cglib建立代理物件。 到這裡我們就對上面的第二個問題有了答案。下面我們進入最後一個問題

springAOP產生的代理物件是怎麼執行的

通過上面的建立代理物件的過程我們知道JdkDynamicAopProxy這個類其實就是動態代理中InvocationHandler的實現類,我們看到這個類的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;
		Object target = null;

		try {
			。。。

			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.
			
			// 檢查是否我們有一些通知。如果我們沒有,我們可以直接對目標類進行反射呼叫,避免建立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...
			    //執行執行鏈
				invocation = new ReflectiveMethodInvocation(proxy,target,args,targetClass,chain);
				
				// Proceed to the joinpoint through the interceptor chain.
				// 進入連線點
				// 執行AOP的攔截過程
				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);
			}
		}
	}
複製程式碼

這個執行過程一共有下面幾個步驟

  • 獲取執行鏈,this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,targetClass)
    • 獲取執行鏈其實就是遍歷我們之前解析到的所有的AspectJPointcutAdvisor,然後通過裡面的AspectJExpressionPointcut匹配到對應的類
      • 如果是動態呼叫則把通知增強類(5中增強類)和方法匹配類MethodMatcher封裝成一個InterceptorAndDynamicMethodMatcher物件到列表中
      • 如果不是的話就直接新增到連結串列中
    • 最後返回.具體程式碼可以跟進去看DefaultAdvisorChainFactory的getInterceptorsAndDynamicInterceptionAdvice方法
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
			Advised config,@Nullable Class<?> targetClass) {

		// This is somewhat tricky... We have to process introductions first,// but we need to preserve order in the ultimate list.
		List<Object> interceptorList = new ArrayList<>(config.getAdvisors().length);
		Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
		boolean hasIntroductions = hasMatchingIntroductions(config,actualClass);
		AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();

		for (Advisor advisor : config.getAdvisors()) {
			if (advisor instanceof PointcutAdvisor) {
				// Add it conditionally.
				PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
				if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
					MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
					MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
					if (MethodMatchers.matches(mm,actualClass,hasIntroductions)) {
						if (mm.isRuntime()) {
							// Creating a new object instance in the getInterceptors() method
							// isn't a problem as we normally cache created chains.
							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;
				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;
	}
複製程式碼
  • 判斷執行鏈如果是空的,那麼說明不用攔截直接通過凡是執行目標類的方法
AopUtils.invokeJoinpointUsingReflection(target,argsToUse)
複製程式碼
  • 如果執行鏈不為空,我們就把需要的資訊封裝到ReflectiveMethodInvocation中呼叫對應的process方法
public Object proceed() throws Throwable {
		// We start with an index of -1 and increment early.
		//如果執行到執行連的最後,則直接呼叫目標方法
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			return invokeJoinpoint();
		}

		// 得到集合中的 MethodInterceptor
		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;
			// 是否是匹配對應的方法
			if (dm.methodMatcher.matches(this.method,this.targetClass,this.arguments)) {
				// 如果匹配呼叫 MethodInterceptor的invoke方法
				// 注意這裡傳入的引數是this 我們下面看一下 ReflectiveMethodInvocation的型別
				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.
			// 說明是適用於此目標方法的 直接呼叫 MethodInterceptor的invoke方法
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}

複製程式碼

這裡會遞迴呼叫型別遍歷執行鏈,如果是動態呼叫則每一次都需要匹配是否對應的方法,是則呼叫invoke方法,如果不是動態呼叫則直接呼叫對應的invoke方法。最後執行完成後返回結果。 而這裡的invoke方法就是我們之前獲取的5個增強方法,我們可以拿其中一個看下,就可以瞭解整個鏈執行的過程。首先我們看下AspectJAfterAdvice的invoke方法

@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		try {
			// 執行切入點方法
			return mi.proceed();
		}
		finally {
			// 呼叫最終通知的增強類方法
			invokeAdviceMethod(getJoinPointMatch(),null,null);
		}
	}
複製程式碼

因為是after方法,所有必須先把目標方法呼叫完之後才呼叫,因此如果執行鏈執行到這個方法,就會再次遞迴繼續執行知道目標方法呼叫完成最後呼叫這個invokeAdviceMethod方法,我們可以跟進去看下,最終會調到AbstractAspectJAdvice的invokeAdviceMethodWithGivenArgs方法

protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
		Object[] actualArgs = args;
		if (this.aspectJAdviceMethod.getParameterCount() == 0) {
			actualArgs = null;
		}
		try {
			ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
			// TODO AopUtils.invokeJoinpointUsingReflection
			// 利用反射,執行增強方法
			// method.invoke(obj,arg)
			return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(),actualArgs);
		}
		catch (IllegalArgumentException ex) {
			throw new AopInvocationException("Mismatch on arguments to advice method [" +
					this.aspectJAdviceMethod + "]; pointcut expression [" +
					this.pointcut.getPointcutExpression() + "]",ex);
		}
		catch (InvocationTargetException ex) {
			throw ex.getTargetException();
		}
	}
複製程式碼

這個方法的本質其實就是利用反射去執行增強方法

最後

至此我們就把springAOP的呼叫過程閱讀完畢,主要流程就是獲取呼叫JdkDynamicAopProxy的invoke方法,獲取執行鏈,封裝成ReflectiveMethodInvocation物件遞迴呼叫process方法,在這個方法中呼叫對應的增強類的invock方法,最終利用反射去執行增強方法。

結語

經過這兩篇文章,我們就把springAOP主體流程的原始碼一起閱讀了一遍,整個過程還是比較複雜的,其中類的關係也是比較亂,大家需要多加理解。到這裡我們就將springIOC和AOP的原始碼基本都閱讀完畢.大家在閱讀的過程中如果有什麼問題或者建議和意見可以直接給我留言,我也會不斷的改進和回答大家的問題,我希望來閱讀我的文章的同學都能從裡面得到幫助,這才是我最想要的。最後預告下,接下去我們會進入SpringMvc的原始碼閱讀希望,大家繼續關注,謝謝觀看!