Spring源碼分析(七)SpringAOP生成代理類及執行的過程

1、AOP的入口

上一節咱們在分析解析AOP標籤的時候,第一步就是註冊了一個類AspectJAwareAdvisorAutoProxyCreator,咱們說它是AOP的入口類。爲何這樣說呢? 來看它父類的父類AbstractAutoProxyCreator,它繼承了BeanPostProcessor接口。 那麼,有兩個方法確定要被調用到postProcessBeforeInitialization、postProcessAfterInitialization。一個在依賴注入完成以前調用,一個在以後調用。spring

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
	if (bean != null) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		if (!this.earlyProxyReferences.contains(cacheKey)) {
			return wrapIfNecessary(bean, beanName, cacheKey);
		}
	}
	return bean;
}
複製代碼

wrapIfNecessary方法則是真正產生代理的地方,咱們先看下它的內部實現。bash

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
	// Create proxy if we have advice.
	//先看它的註釋,大意說:若是有通知,就建立代理。
	//其實就是在bean.getClass()找到全部的通知和advisor
	//這裏面其實又分爲兩個步驟:
	//第一,在Bean工廠找到全部的Advisor 第二,根據beanClass和Pointcut去作匹配
	Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
	if (specificInterceptors != DO_NOT_PROXY) {
		this.advisedBeans.put(cacheKey, Boolean.TRUE);
		//真正建立代理並返回,這時候返回的就是代理類了,把真實的bean替換掉
		Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
		this.proxyTypes.put(cacheKey, proxy.getClass());
		return proxy;
	}
	this.advisedBeans.put(cacheKey, Boolean.FALSE);
	return bean;
}
複製代碼

看到上面的源碼,整個過程就分爲了兩步,匹配和建立返回。這樣就完成了代理類的替換,是否是有點偷樑換柱的感受。post

一、匹配

先是找到全部的Advisor,這個比較簡單。由於咱們知道,在解析的時候就把配置的通知封裝成advisor註冊了進去。首先拿到beanDefinitionNames容器全部beanName,循環判斷bean的類型是否是advisor接口的類型,符合條件返回。ui

public List<Advisor> findAdvisorBeans() {
	// Determine list of advisor bean names, if not cached already.
	String[] advisorNames = null;
	
	//先拿到beanName
	advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
			this.beanFactory, Advisor.class, true, false);
		
	List<Advisor> advisors = new LinkedList<Advisor>();
	for (String name : advisorNames) {
		if (isEligibleBean(name)) {		
			try {
				//再從Bean工廠中拿bean的實例
				advisors.add(this.beanFactory.getBean(name, Advisor.class));
			}
		}
	}
	//返回的advisors就是配置文件中全部的advice和advisor
	return advisors;
}
複製代碼

找到advisors並未結束,還要跟pointcut作匹配,看這些advisor符不符合表達式條件。它最終調用到Pointcut的match方法。這個方法嵌套的太深,就不貼代碼了,核心思想就是判斷class和class對應的method是否與pointcut表達式匹配。this

二、建立代理

通過上面查找匹配後,肯定當前的bean確實須要代理,就調用createProxy方法。lua

  • 設置代理工廠
protected Object createProxy(Class<?> beanClass, 
		String beanName, Object[] specificInterceptors, TargetSource targetSource) {
	//建立代理工廠
	ProxyFactory proxyFactory = new ProxyFactory();
	// Copy our properties (proxyTargetClass etc) inherited from ProxyConfig.
	proxyFactory.copyFrom(this);

	//將beanClass上的接口設置到代理工廠
	evaluateProxyInterfaces(beanClass, proxyFactory);
		
	//設置Advisor到代理工廠
	Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
	for (Advisor advisor : advisors) {
		proxyFactory.addAdvisor(advisor);
	}
	//設置目標對象
	proxyFactory.setTargetSource(targetSource);
	return proxyFactory.getProxy(this.proxyClassLoader);
}
複製代碼
  • 建立

建立代理分爲JDK的代理和Cglib的代理,這裏咱們先關注JDK的代理。getProxy方法就調用到JdkDynamicAopProxy類的方法。這個類還實現了InvocationHandler接口,說明它同時仍是調用處理程序。即在調用代理類的invoke方法時,實際上就會調用到JdkDynamicAopProxy.invoke()spa

public Object getProxy(ClassLoader classLoader) {
	//這裏又給代理工廠加了兩個接口 SpringProxy和Advised
	Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
	
	//這個方法就比較熟悉了,正是JDK動態代理的方法
	//這裏的this就是JdkDynamicAopProxy實例。
	return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
複製代碼

2、代理類的調用

在實例化Bean和完成依賴注入後,會判斷當前的Bean是否須要代理,若是須要,就生成代理類把原始類替換掉。在業務方法裏面調用的時候,就會調用到JdkDynamicAopProxy.invoke()debug

在執行invoke的時候,咱們又能夠分爲兩個步驟...-_-||,是否是跟二頗有緣,每次都是兩個步驟。。代理

一、獲取方法的攔截鏈

首先從代理工廠中拿到全部的advisor,而後判斷是否是PointcutAdvisor類型,其次先matches一下targetClass,再matches一下method,證實這個類的方法在pointcut範圍內,加入interceptorList,最後返回。code

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
			Advised config, Method method, Class<?> targetClass) {
	List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
	//config就是代理工廠的實例
	for (Advisor advisor : config.getAdvisors()) {
		if (advisor instanceof PointcutAdvisor) {
			PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
			if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
				MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
				MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
				if (MethodMatchers.matches(mm, method, targetClass, hasIntroductions)) {
					interceptorList.addAll(Arrays.asList(interceptors));
				}
			}
		}
	}
	return interceptorList;
}
複製代碼

二、調用

拿到方法的攔截鏈,而後調用。調用的時候有個地方比較有意思。它先建立了一個對象ReflectiveMethodInvocation。這個對象有兩個參數

currentInterceptorIndex   //當前調用的攔截器的索引,默認值-1
interceptorsAndDynamicMethodMatchers   //攔截器的列表
複製代碼

而後看它的調用方法。

public Object proceed() throws Throwable {
	
	//若是倆個變量相等,說明已經調用完了全部的攔截器
	if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
		//執行被代理方法
		return invokeJoinpoint();
	}
	//從-1開始,每次累加1。interceptorOrInterceptionAdvice就是對應的每個通知
	//好比before、after、after-returning...
	//對應的實例類分別是:
	//MethodBeforeAdviceInterceptor、AspectJAfterAdvice、AfterReturningAdviceInterceptor
	Object interceptorOrInterceptionAdvice =
			this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
			
	
	//在調用具體通知的invoke方法時,把當前對象當參數傳了過去。
	//由於在具體的通知執行以後還要回調回來,執行當前對象的proceed().
	//這樣就造成了一個通知調用鏈,當全部的通知執行完畢,調用上面的invokeJoinpoint()
	return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
複製代碼

看完它的調用流程,咱們還有一個疑問。通知一共有5種類型,前置通知、後置通知、方法返回後通知、環繞通知、異常通知。那麼,它是怎麼保證調用順序的呢? 咱們來看兩個通知類裏面具體的實現。

前置通知
public Object invoke(MethodInvocation mi) throws Throwable {
	//這個比較簡單,執行完before就回調
	this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
	return mi.proceed();
}
複製代碼
後置通知
public Object invoke(MethodInvocation mi) throws Throwable {
	//這個就比較有趣了,它先執行回調
	//經過finally機制再執行本身的通知方法
	try {
		return mi.proceed();
	}
	finally {
		invokeAdviceMethod(getJoinPointMatch(), null, null);
	}
}
複製代碼
環繞通知
//環繞通知會調用到切面類的自定義方法
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
	System.out.println("環繞通知以前");
	//能夠本身判斷還要不要回調回去。
	//回調回去的話,接着循環調用鏈
	//若是再也不回調,就要看鏈的順序了,在它以後的就再也不執行
	Object result = joinPoint.proceed();
	System.out.println("環繞通知以後");
	return result;
}
複製代碼

3、基於註解的AOP

若是想使用註解AOP,須要開啓一個配置。<aop:aspectj-autoproxy />。Spring解析配置標籤的時候,調用到AspectJAutoProxyBeanDefinitionParser.parse()。 這個方法主要就幹了一件事:註冊入口類AnnotationAwareAspectJAutoProxyCreator。 XML配置方式的AOP,Spring註冊的入口類叫作AspectJAwareAdvisorAutoProxyCreator

它們的調用流程是同樣的,都是在實例化以後調用爺爺類的AbstractAutoProxyCreator.postProcessAfterInitialization()。回憶一下,wrapIfNecessary方法是真正產生代理的地方。它先獲取全部的通知並與當前的bean class匹配,若是有,就說明當前的Bean須要代理,則產生代理類。咱們知道,在XML配置方式的AOP中,Spring把配置的通知都封裝成advisor註冊到容器裏,因此在獲取的時候,直接在Bean工廠中匹配Advisor類型的Bean就行。 可是,在解析註解AOP的時候,咱們看到它只是註冊了一個入口類而已呀,並無註冊advisor,那麼,在這裏怎麼獲取呢?

目光回到查詢advisor的方法。

protected List<Advisor> findCandidateAdvisors() {
	// Add all the Spring advisors found according to superclass rules.
	// 這個是查詢XML配置方式的advoisor
	List<Advisor> advisors = super.findCandidateAdvisors();
	// Build Advisors for all AspectJ aspects in the bean factory.
	// 這個就是查詢註解方式的advisor
	advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
	return advisors;
}
複製代碼

buildAspectJAdvisors方法查詢advisor的時候,它大體能夠分3個步驟。

一、從Bean工廠獲取全部的beanName

String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);

二、循環beanNames,判斷bean是否包含Aspect註解

for (String beanName : beanNames) {
	Class<?> beanType = this.beanFactory.getType(beanName);
	if (this.advisorFactory.isAspect(beanType)) {
		aspectNames.add(beanName);
		......
	}
}
複製代碼

三、獲取Advisors

先拿到Class對象上的全部Method對象,根據Method對象的Annotation類型,返回不一樣的Advice對象。這個跟XML方式返回的Advice對象是同樣的。

switch (aspectJAnnotation.getAnnotationType()) {
	case AtBefore:
		springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, ajexp, aif);
		break;
	case AtAfter:
		springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, ajexp, aif);
		break;
	case AtAfterReturning:
		springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, ajexp, aif);
		AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
		if (StringUtils.hasText(afterReturningAnnotation.returning())) {
			springAdvice.setReturningName(afterReturningAnnotation.returning());
		}
		break;
	case AtAfterThrowing:
		springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, ajexp, aif);
		AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
		if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
			springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
		}
		break;
	case AtAround:
		springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, ajexp, aif);
		break;
	case AtPointcut:
		if (logger.isDebugEnabled()) {
			logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
		}
		return null;
	default:
		throw new UnsupportedOperationException(
				"Unsupported advice type on method " + candidateAdviceMethod);
}
複製代碼

最後將advice和pointcut封裝成InstantiationModelAwarePointcutAdvisorImpl對象返回。返回以後,建立代理類進行替換。

由此看來,註解方式的AOP,是在查詢Advisors的時候纔去解析並生成的,其餘的與XML的配置方式處理流程都是同樣的。

4、 總結

本章節咱們重點闡述了3個問題。即怎樣產生代理類、代理類的執行過程、AnnotationAOP的處理流程。結合本章節和上一章節的內容,可能使咱們對Spring AOP的內部處理流程加深了印象。

  • 怎樣產生代理類? 經過循環beanNames實例化Bean對象,判斷此對象是否與pointcut表達式匹配。若是匹配就根據advice生成不一樣的advisor對象,而後調用JDK或者CGLIB的方法生成代理類返回。

  • 代理類執行 調用JDK或者CGLIB的invoke方法,查詢advisor的調用鏈。鏈式調用,根據通知類型調用不一樣的advice實現加強。

相關文章
相關標籤/搜索