Spring 之 AOP 詳解

AOP 概念

AOP(Aspect Oriented Programming)面向切面編程是 Spring 框架最核心的組件之一,它通過對程序結構的另一種考慮,補充了 OOP(Object-Oriented Programming)面向對象編程。在 OOP 中模塊化的關鍵單元是類,而在 AOP 中,模塊化單元是切面。也就是說 AOP 關注的不再是類,而是一系列類裏面需要共同能力的行爲。
在 AOP 中模塊化單元是切面(Aspect),它將那些影響多個類的共同行爲封裝到可重用的模塊中,然後你就可以決定在什麼時候對哪些類的哪些行爲執行進行攔截(切點),並使用封裝好的可重用模塊裏面的行爲(通知)對其攔截的業務行爲進行功能增強,而不需要修改業務模塊的代碼,切面就是對此的一個抽象描述。

AOP 中有以下基礎概念:

  • Join point(連接點):程序執行期間的某一個點,例如執行方法或處理異常時候的點。在 Spring AOP中,連接點總是表示方法的執行。
  • Advice(通知):通知是指一個切面在特定的連接點要做的事情。通知分爲方法執行前通知,方法執行後通知,環繞通知等。許多 AOP框架(包括 Spring)都將通知建模爲攔截器,在連接點周圍維護一系列攔截器(形成攔截器鏈),對連接點的方法進行增強。
  • Pointcut(切點):一個匹配連接點(Join point)的謂詞表達式。通知(Advice)與切點表達式關聯,並在切點匹配的任何連接點(Join point)(例如,執行具有特定名稱的方法)上運行。切點是匹配連接點(Join point)的表達式的概念,是AOP的核心,並且 Spring 默認使用 AspectJ 作爲切入點表達式語言。
  • Aspect(切面):它是一個跨越多個類的模塊化的關注點,它是通知(Advice)和切點(Pointcut)合起來的抽象,它定義了一個切點(Pointcut)用來匹配連接點(Join point),也就是需要對需要攔截的那些方法進行定義;它定義了一系列的通知(Advice)用來對攔截到的方法進行增強;
  • Target object(目標對象):被一個或者多個切面(Aspect)通知的對象,也就是需要被 AOP 進行攔截對方法進行增強(使用通知)的對象,也稱爲被通知的對象。由於在 AOP 裏面使用運行時代理,所以目標對象一直是被代理的對象。
  • AOP proxy(AOP 代理):爲了實現切面(Aspect)功能使用 AOP 框架創建一個對象,在 Spring 框架裏面一個 AOP 代理要麼指 JDK 動態代理,要麼指 CgLIB 代理。
  • Weaving(織入):是將切面應用到目標對象的過程,這個過程可以是在編譯時(例如使用 AspectJ 編譯器),類加載時,運行時完成。Spring AOP 和其它純 Java AOP 框架一樣,是在運行時執行植入。
  • Advisor:這個概念是從 Spring 1.2的 AOP 支持中提出的,一個 Advisor 相當於一個小型的切面,不同的是它只有一個通知(Advice),Advisor 在事務管理裏面會經常遇到。
  • Before Advice(前置通知):在某連接點(Join point)之前執行的通知,但是這個通知不能阻止連接點前的執行。
  • After Advice(後置通知):當某連接點(Join point)退出的時候執行的通知(不論正常返回還是異常退出)。
  • After Return Advice(返回後通知):當某連接點(Join point)正常完成的時候執行的通知,不包括拋出異常的情況。
  • Around Advice(環繞通知):包圍一個連接點(Join point)的通知,類似 Web 中 Servlet 規範中的 Filter 的 doFilter 方法。可以在方法調用前後完成自定義行爲,也可以選擇不執行。
  • After Throwing Advice(返回後通知):當某連接點(Join point)拋出異常退出時執行的通知。

可以將多個通知應用到同一個目標對象上,即可以將多個切面織入同一個目標對象。
相比 OOP,AOP 有以下優點:

  • 業務代碼更加簡潔,例如當需要在業務行爲前後做一些事情時候,只需要在該行爲前後配置切面進行處理,無須修改業務行爲代碼。
  • 切面邏輯封裝性好,並且可以被複用,例如我們可以把打日誌的邏輯封裝爲一個切面,那麼我們就可以在多個相關或者不相關的類的多個方法上配置該切面。

AOP 源碼分析

關於 AOP 的使用有 XML 配置方式和註解配置方式,本文就不再闡述使用教程。

尋找入口

Spring 的 AOP 是通過接入 BeanPostProcessor 後置處理器開始的,它是 Spring IOC 容器經常使用到的一個特性,這個 Bean 後置處理器是一個監聽器,可以監聽容器觸發的 Bean 聲明週期事件。後置處理器向容器註冊以後,容器中管理的 Bean 就具備了接收 IOC 容器事件回調的能力。
BeanPostProcessor 的使用非常簡單,只需要繼承並實現方法即可。

public interface BeanPostProcessor {
	//爲在 Bean 的初始化前提供回調入口
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
     	return bean;
    }
	//爲在 Bean 的初始化之後提供回調入口
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
     	return bean;
    }
}

這兩個回調的入口都是和容器管理的 Bean 的生命週期事件緊密相關,可以爲用戶提供在 Spring IOC 容器初始化 Bean 過程中自定義的處理操作。
所以尋找 AOP 的入口,我們需要先定位到 BeanPostProcessor 執行的代碼。即 AbstractAutowireCapableBeanFactory 類的 initializeBean 方法,在執行初始化方法前後調用 BeanPostProcessor 後置處理器的回調方法。

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
		//JDK的安全機制驗證權限
		if (System.getSecurityManager() != null) {
			//通過匿名內部類根據實例化策略創建實例對象
			AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
				invokeAwareMethods(beanName, bean);
				return null;
			}, getAccessControlContext());
		}
		else {
			//將實例化的對象信息封裝起來,如bean名稱,類加載器,所屬容器等信息
			invokeAwareMethods(beanName, bean);
		}

		Object wrappedBean = bean;
		//調用BeanPostProcessor後置處理器的回調方法,在Bean實例初始化前做一些處理
		if (mbd == null || !mbd.isSynthetic()) {
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}

		//通過反射調用Bean實例的初始化方法,這個初始化方法是在init-method指定的
		try {
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					(mbd != null ? mbd.getResourceDescription() : null),
					beanName, "Invocation of init method failed", ex);
		}
		//調用BeanPostProcessor後置處理器的回調方法,在Bean實例初始化之後做一些處理
		if (mbd == null || !mbd.isSynthetic()) {
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

		return wrappedBean;
	}

因爲要生成目標對象的代理類,所以肯定是在 Bean 初始化方法執行之後做的操作。所以跟進 applyBeanPostProcessorsAfterInitialization 方法,可以看到遍歷所有的 BeanPostProcessor 後置處理器中初始化後的處理方法。

	public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
			throws BeansException {

		Object result = existingBean;
		//遍歷容器爲所創建的Bean添加所有BeanPostProcessor後置處理器
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			//調用Bean實例所有的後置處理中初始化後的處理方法,爲Bean實例對象在初始化之後做一些自定義的處理
			Object current = processor.postProcessAfterInitialization(result, beanName);
			if (current == null) {
				return result;
			}
			result = current;
		}
		return result;
	}

BeanPostProcessor 是一個接口,其初始化前的操作方法和初始化後的操作方法均委託其實現子類來實現,在 Spring 中 BeanPostProcessor 的實現子類非常的多,分別完成不同的操作,如:AOP 面向切面編程的註冊通知適配器、Bean 對象的數據校驗、Bean 繼承屬性、方法的合併等等,我們以最簡單的 AOP 切面織入來簡單瞭解其主要的功能。下面我們來分析其中一個創建 AOP 代理對象的子類 AbstractAutoProxyCreator 類。該類重寫了 postProcessAfterInitialization()方法。

選擇代理策略

AbstractAutoProxyCreator 類的 postProcessAfterInitialization 方法,主要是進行判斷早期暴露的引用 bean 對象是否等於當前 bean 對象,如果不是則重新生成代理對象。

	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

從這裏我們可以看出,以上尋找 AOP 生成代理對象的入口是不準確的。爲了解決屬性注入的循環依賴問題,Spring 會在實例化對象之後、屬性依賴注入之前先暴露 bean 對象。所以在 AbstractAutowireCapableBeanFactory 類的 getEarlyBeanReference 方法上就已經生成了代理對象,源碼如下:

	protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
		Object exposedObject = bean;
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
					SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
					exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
				}
			}
		}
		return exposedObject;
	}

指向了 AbstractAutoProxyCreator 類的 getEarlyBeanReference 方法,通過源碼我們可以看到一樣是調用了 wrapIfNecessary 方法。所以可以確定 wrapIfNecessary 方法就是生成 AOP 代理類的入口。

	public Object getEarlyBeanReference(Object bean, String beanName) {
		Object cacheKey = getCacheKey(bean.getClass(), beanName);
		this.earlyProxyReferences.put(cacheKey, bean);
		return wrapIfNecessary(bean, beanName, cacheKey);
	}

生成代理對象

繼續追蹤 wrapIfNecessary 方法,可以看到主要是對 bean 對象做判斷是否需要生成代理類

	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		//判斷是否應該代理這個Bean
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		/**
		 * 判斷是否是一些InfrastructureClass或者是否應該跳過這個Bean,所謂InfrastructureClass就是指
		 * Advice、PointCut、Advisor等接口的實現類,shouldSkip方法默認返回false,子類可以覆蓋
		 */
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}

		//獲取這個Bean的通知,是否需要進行代理
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if (specificInterceptors != DO_NOT_PROXY) {
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			//創建代理
			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;
	}

根進 createProxy 方法, 主要對 ProxyFactory 做一些屬性設置。

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

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

		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.copyFrom(this);

		if (!proxyFactory.isProxyTargetClass()) {
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}

		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		proxyFactory.addAdvisors(advisors);
		proxyFactory.setTargetSource(targetSource);
		customizeProxyFactory(proxyFactory);

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

		return proxyFactory.getProxy(getProxyClassLoader());
	}

整個過程跟下來,發現最終調用的是 proxyFactory.getProxy()方法。到這裏大概能夠猜到 proxyFactory 有 JDK 和 CGLib 的,那麼該如何選擇呢?

	public Object getProxy(@Nullable ClassLoader classLoader) {
		//獲取代理類生成器並通過代理類生生成器生成代理對象
		return createAopProxy().getProxy(classLoader);
	}
	//獲取 AOP 代理類生成器
	protected final synchronized AopProxy createAopProxy() {
		if (!this.active) {
			activate();
		}
		return getAopProxyFactory().createAopProxy(this);
	}

跟蹤 createAopProxy 方法可以看到是如何選擇 JDK 或 CGLib 作爲生成器,如果沒有指定使用 Cglib 並且被代理類有接口的情況下使用 JDK 動態代理方式,否則使用 CGLib動態代理方式。在本文以 JDK 動態代理作爲跟蹤,如果對 JDK 動態代理不瞭解的同學可以看JDK動態代理實現原理

	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		//判斷是否指定使用Cglib方式、是否有接口實現等來決定生成代理類的方式
		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);
			}
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}

獲取到生成器之後,就調用 getProxy 方法獲取代理對象。跟進到 JdkDynamicAopProxy 類的 getProxy 方法,如下:

	public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
		}
		//獲取指定代理對象的完整接口集
		Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
		//檢查接口集中有沒有實現equals和hashcode方法
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
		//創建代理對象,具體織入代碼邏輯需要看invoke方法
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
	}

至此完成代理對象的創建,通過註釋我們應該已經看得非常明白代理對象的生成過程,此處不再贅述。下面的問題是,代理對象生成了,那切面是如何織入的?

調用代理方法

InvocationHandler 是 JDK 動態代理的核心,生成的代理對象的方法調用都會委託到 InvocationHandler.invoke() 方法。而從 JdkDynamicAopProxy 的源碼我們可以看到這個類其實也實現了 InvocationHandler,下面我們分析 Spring AOP 是如何織入切面的,直接碼看 invoke 方法源碼:

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

		//獲取到bean容器實例化的源對象
		TargetSource targetSource = this.advised.targetSource;
		Object target = null;

		try {
			//目標對象未實現equals
			if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
				return equals(args[0]);
			}
			//目標對象未實現hashcode
			else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
				return hashCode();
			}

			else if (method.getDeclaringClass() == DecoratingProxy.class) {
				return AopProxyUtils.ultimateTargetClass(this.advised);
			}
			//直接反射調用Advised接口或者其父接口中定義的方法,不應用通知
			else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
					method.getDeclaringClass().isAssignableFrom(Advised.class)) {
				return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
			}

			Object retVal;

			if (this.advised.exposeProxy) {
				oldProxy = AopContext.setCurrentProxy(proxy);
				setProxyContext = true;
			}

			//獲得目標對象的類
			target = targetSource.getTarget();
			Class<?> targetClass = (target != null ? target.getClass() : null);

			//獲取可以應用到此方法中的攔截器列表
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

			
			//如果沒有可以應用到此方法上的攔截器,則直接反射調用method.invoke
			if (chain.isEmpty()) {
				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
			}
			else {
				//創建攔截鏈調用器
				MethodInvocation 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())) {
				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()) {
				targetSource.releaseTarget(target);
			}
			if (setProxyContext) {
				AopContext.setCurrentProxy(oldProxy);
			}
		}
	}

主要實現思路可以簡述爲:首先獲取應用到此方法上的通知鏈(Interceptor Chain)。如果有通知,則應用通知,並執行 JoinPoint;如果沒有通知,則直接反射執行 JoinPoint。而這裏的關鍵是通知鏈是如何獲取的以及它又是如何執行的呢?
首先,從上面的代碼可以看到,通知鏈是通過 Advised.getInterceptorsAndDynamicInterceptionAdvice()這個方法來獲取的,我們來看下這個方法的實現邏輯:

	public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable 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;
	}

攔截器調用鏈是通過 AdvisorChainFactory 的 getInterceptorsAndDynamicInterceptionAdvice 方法來完成的,繼續跟進:

	public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
			Advised config, Method method, @Nullable Class<?> targetClass) {

		//註冊一系列的AdvisorAdapter,用於將Advisor轉化爲MethodInterceptor
		AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
		Advisor[] advisors = config.getAdvisors();
		//存儲可以應用到指定類或方法的攔截器
		List<Object> interceptorList = new ArrayList<>(advisors.length);
		Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
		//查看是否包含IntroductionAdvisor
		Boolean hasIntroductions = null;

		for (Advisor advisor : advisors) {
			if (advisor instanceof PointcutAdvisor) {
				// Add it conditionally.
				PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
				//檢查當前Advisor的切入點是否過濾掉了當前類
				if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
					//檢查當前Advisor的切入點是否可以匹配當前方法
					MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
					boolean match;
					if (mm instanceof IntroductionAwareMethodMatcher) {
						if (hasIntroductions == null) {
							hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
						}
						match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
					}
					else {
						match = mm.matches(method, actualClass);
					}
					if (match) {
						//將Advisor轉化爲MethodInterceptor
						MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
						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;
				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;
	}

我們來看看適配器是如何將Advisor轉化爲MethodInterceptor,源碼如下:

public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {

	private final List<AdvisorAdapter> adapters = new ArrayList<>(3);

	public DefaultAdvisorAdapterRegistry() {
		registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
		registerAdvisorAdapter(new AfterReturningAdviceAdapter());
		registerAdvisorAdapter(new ThrowsAdviceAdapter());
	}


	@Override
	public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
		//.....
	}

	@Override
	public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
		List<MethodInterceptor> interceptors = new ArrayList<>(3);
		Advice advice = advisor.getAdvice();
		if (advice instanceof MethodInterceptor) {
			interceptors.add((MethodInterceptor) advice);
		}
		//對通知進行判斷,獲取實際的方法攔截器
		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[0]);
	}

	@Override
	public void registerAdvisorAdapter(AdvisorAdapter adapter) {
		this.adapters.add(adapter);
	}

}

DefaultAdvisorAdapterRegistry 是 AdvisorAdapterRegistry 適配器的默認實現,創建過程中會先注入三個轉換的具體適配器。在一開始我們已經說過有五種通知,分別是:

  • 前置通知
  • 後置通知
  • 返回後通知
  • 環繞通知
  • 異常通知

根據通知的具體作用,可以知道前置通知、返回後通知、異常通知是依賴於代理方法的執行時間段和結果的。而後置通知只要代理方法執行就必須調用,環繞通知可以決定什麼時候調用代理方法。
我們先來看下這些通知的 UML 類圖
在這裏插入圖片描述
可以看到 AdvisorAdapter 的作用就是創建具體的 MethodInterceptor,攔截器鏈就是由 MethodInterceptor 組成的。前置通知、返回後通知、異常通知需要由 AdvisorAdapter 適配器進行轉換是爲了方便後期依賴於代理方法不同時間段和結果的擴展。我們可以通過 AspectJAfterAdvice 類看到所有通知的具體實現。
到此我們明白了攔截器鏈(調用鏈)是如何生成的,接下來看看具體是如何調用的。由 ReflectiveMethodInvocation.proceed() 方法實現,生成 ReflectiveMethodInvocation 對象時已經對代理類、代理方法等數據進行保存。源碼如下:

	protected ReflectiveMethodInvocation(
			Object proxy, @Nullable Object target, Method method, @Nullable Object[] arguments,
			@Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {

		this.proxy = proxy;
		this.target = target;
		this.targetClass = targetClass;
		this.method = BridgeMethodResolver.findBridgedMethod(method);
		this.arguments = AopProxyUtils.adaptArgumentsIfNecessary(method, arguments);
		this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
	}
	
	public Object proceed() throws Throwable {
		// 執行攔截器已經執行完,執行目標方法
		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
			return invokeJoinpoint();
		}

		//獲取指定位置的攔截器
		Object interceptorOrInterceptionAdvice =
				this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
		//是否需要進行動態匹配joinPoint
		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
			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 {
				// 跳過此攔截器並調用鏈中的下一個攔截器
				return proceed();
			}
		}
		else {
			// 它是一個攔截器,因此我們只需要調用它:切入點將在構造此對象之前進行靜態評估。
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}

可以很明顯的看到這裏使用的是責任鏈模式,層層往下執行,當所有的攔截器都執行完成後開始執行代理方法。如果需要依賴於代理方法的執行時間或結果的攔截器,具體代碼實現在攔截器中。這裏我們以 AfterReturningAdviceInterceptor 類進行分析,進入其 proceed 方法:

	public Object invoke(MethodInvocation mi) throws Throwable {
		Object retVal = mi.proceed();
		this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
		return retVal;
	}

因爲當前是返回後通知,需要等代理方法執行完成後才能調用。所以返回到調用鏈上繼續執行,獲取到代理方法的執行結果後開始調用 AspectJAfterReturningAdvice.afterReturning() 方法:

	public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
		//僅在返回值是給定返回類型的實例和泛型類型參數(如果有)與賦值規則匹配的情況下才調用通知。如果返回類型爲Object,則始終會調用
		if (shouldInvokeOnReturnValueOf(method, returnValue)) {
			invokeAdviceMethod(getJoinPointMatch(), returnValue, null);
		}
	}

繼續跟進 invokeAdviceMethod 方法可以看到調整到父類 AspectJAfterAdvice 中進行實現:

	protected Object invokeAdviceMethod(
			@Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable ex)
			throws Throwable {
		//argBinding主要是按照通知方法的入參順序拼接入參,然後調用通知方法
		return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
	}

	protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
		Object[] actualArgs = args;
		//如果通知方法沒有入參,就設置爲null
		if (this.aspectJAdviceMethod.getParameterCount() == 0) {
			actualArgs = null;
		}
		try {
			ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
			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();
		}
	}

所有通知方法的執行最終都會走到 AspectJAfterAdvice.invokeAdviceMethod() 方法上,通過反射調用通知方法。

總結

本文從查找 AOP 入口開始層層分析源碼到最後整個調用鏈的執行,希望能夠幫助讀者瞭解 AOP 的實現原理。AOP 常用場景有事務管理、日誌打印、權限管理、性能監控等方面,瞭解 AOP 原理有利於讀者在使用 AOP 時有更爲深入的應用。

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