重拾-Spring-AOP

斷風雨發表於2019-03-03

AOP 術語

關於 AOP 的概念描述及相關術語可以參考 徹底征服 Spring AOP 之 理論篇 總結的很好; 本文將著重分析下 AOP 的實現過程。

使用示例

定義介面

public interface UserService {
    void say ();
}
複製程式碼

介面實現類如下:

public class UserServiceImpl implements UserService {
    public void say() {
        System.out.println("do say method");
    }
}
複製程式碼

定義通知

public class UserAdvice implements MethodBeforeAdvice {

    public void before(Method m, Object[] args, Object target) throws Throwable {
        System.out.println("do before advice ....");
    }
}
複製程式碼

配置 AOP

<beans>
	<!-- 配置介面實現類 -->
    <bean id="userService" class="org.springframework.aop.UserServiceImpl" />

    <!-- 配置通知類 -->
    <bean id="userAdvice" class="org.springframework.aop.UserAdvice" />

    <!--代理類-->
    <bean id="userProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <!--要代理的介面 建立代理物件時需要-->
        <!-- 配置該屬性會採用 jdk 動態代理,反之採用 cglib -->
        <property name="proxyInterfaces">
            <value>org.springframework.aop.UserService</value>
        </property>
        <!--攔截器名字,也就是我們定義的通知類,可配置多個通知類 -->
        <property name="interceptorNames">
            <list>
                <value>userAdvice</value>
            </list>
        </property>
        <!--目標類,就是我們業務的實現類-->
        <property name="target">
            <ref bean="userService"/>
        </property>
    </bean>
</beans>
複製程式碼

測試

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/org/springframework/aop/aop.xml");

UserService userService = (UserService) ctx.getBean("userProxy");

userService.say();
複製程式碼

執行結果如下:

do before advice ....
do say method
複製程式碼

從執行結果來看,前置通知對介面方法已經起增強作用。 下面我們將看下 Spring AOP 的具體實現。

實現分析

從上面的示例可以看出 Spring AOP 的配置主要基於類 ProxyFactoryBean ,那麼我們就以此為入口去剖析其實現。

ProxyFactoryBean 類結構

ProxyFactoryBean

建立切面鏈

ProxyFactoryBean 的類結構,我們發現其實現了介面 BeanFactoryAware,也就說明在其例項化過程中會呼叫方法 setBeanFactory; 原始碼如下:

public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
	// 設定 beanFactory
	this.beanFactory = beanFactory;
	logger.debug("Set BeanFactory. Will configure interceptor beans...");
	// 建立 advisor chain
	createAdvisorChain();
	logger.info("ProxyFactoryBean config: " + this);
	if (singleton) {
		// Eagerly initialize the shared singleton instance
		getSingletonInstance();
		// We must listen to superclass advice change events to recache singleton
		// instance if necessary
		addListener(this);
	}
}
複製程式碼

setBeanFactory 方法中除了設定 beanFactory , 還有一個重要的動作就是 createAdvisorChain 建立 advisor chain (也可以理解為就是切面鏈)。 那麼下面我們將看下具體是怎樣建立 advisor chain 的。

private void createAdvisorChain() throws AopConfigException, BeansException {
	// 檢測是否配置了 interceptorNames, 也就是是否配置相關 advice 通知; 若沒有配置直接返回
	if (this.interceptorNames == null || this.interceptorNames.length == 0) {
		//throw new AopConfigException("Interceptor names are required");
		return;
	}
	
	// Globals can't be last
	if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX)) {
		throw new AopConfigException("Target required after globals");
	}

	// Materialize interceptor chain from bean names
	for (int i = 0; i < this.interceptorNames.length; i++) {
		String name = this.interceptorNames[i];
		logger.debug("Configuring interceptor '" + name + "'");
		// 判斷 interceptor name 是否以 * 結尾
		if (name.endsWith(GLOBAL_SUFFIX)) {
			if (!(this.beanFactory instanceof ListableBeanFactory)) {
				throw new AopConfigException("Can only use global advisors or interceptors with a ListableBeanFactory");
			}
			else {
				addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
				                 name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
			}
		}
		else {
			// add a named interceptor
			// 獲取 advice bean
			Object advice = this.beanFactory.getBean(this.interceptorNames[i]);
			// 將 advisor 加入到連結串列中
			addAdvisor(advice, this.interceptorNames[i]);
		}
	}
}
複製程式碼
private void addAdvisor(Object next, String name) {
	logger.debug("Adding advisor or TargetSource [" + next + "] with name [" + name + "]");
	// We need to add a method pointcut so that our source reference matches
	// what we find from superclass interceptors.
	// 查詢 advice 通知匹配的 pointcut, 並建立一個 advisor
	Object advisor = namedBeanToAdvisorOrTargetSource(next);
	if (advisor instanceof Advisor) {
		// if it wasn't just updating the TargetSource
		logger.debug("Adding advisor with name [" + name + "]");
		addAdvisor((Advisor) advisor);
		this.sourceMap.put(advisor, name);
	}
	else {
		logger.debug("Adding TargetSource [" + advisor + "] with name [" + name + "]");
		setTargetSource((TargetSource) advisor);
		// save target name
		this.targetName = name;
	}
}
複製程式碼

addAdvisor 方法可以看到,在新增 advisor 前,需要先建立 advisor , 會呼叫方法 namedBeanToAdvisorOrTargetSource

private Object namedBeanToAdvisorOrTargetSource(Object next) {
	try {
		// 將 advice 包裝成一個 advisor
		Advisor adv = GlobalAdvisorAdapterRegistry.getInstance().wrap(next);
		return adv;
	}
	catch (UnknownAdviceTypeException ex) {
		
	}
}
複製程式碼

namedBeanToAdvisorOrTargetSource 方法會呼叫單例模式的 GlobalAdvisorAdapterRegistry 的方法 wrap 將 advice 包裝成一個 advisor; 在檢視 wrap 的實現之前,我們可以先看下 GlobalAdvisorAdapterRegistry 是做什麼的。

public class GlobalAdvisorAdapterRegistry extends DefaultAdvisorAdapterRegistry {
	
	private static GlobalAdvisorAdapterRegistry instance = new GlobalAdvisorAdapterRegistry();
	
	public static GlobalAdvisorAdapterRegistry getInstance() {
		return instance;
	}

	private GlobalAdvisorAdapterRegistry() {
	}	
}

public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry {
	
	private List adapters = new LinkedList();
	
	public DefaultAdvisorAdapterRegistry() {
		// register well-known adapters
		registerAdvisorAdapter(new BeforeAdviceAdapter());
		registerAdvisorAdapter(new AfterReturningAdviceAdapter());
		registerAdvisorAdapter(new ThrowsAdviceAdapter());
	}
}
複製程式碼

從上面 GlobalAdvisorAdapterRegistry 的實現可以看出其採用了單例模式並繼承了類 DefaultAdvisorAdapterRegistry 在構造的過程中內建了 3 種 advice adapter 用於匹配 advice 。 下面我們在看下它是如何 wrap 包裝 advice 的。

public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
	if (adviceObject instanceof Advisor) {
		return (Advisor) adviceObject;
	}
	
	if (!(adviceObject instanceof Advice)) {
		throw new UnknownAdviceTypeException(adviceObject);
	}
	Advice advice = (Advice) adviceObject;
	
	if (advice instanceof Interceptor) {
		// So well-known it doesn't even need an adapter
		return new DefaultPointcutAdvisor(advice);
	}

	// 遍歷內建的 advice adapters
	for (int i = 0; i < this.adapters.size(); i++) {
		// Check that it is supported
		AdvisorAdapter adapter = (AdvisorAdapter) this.adapters.get(i);
		// 判斷當前 adapter 是否支付當前 advice
		if (adapter.supportsAdvice(advice)) {
			// 如果支援的話,返回一個 DefaultPointcutAdvisor
			return new DefaultPointcutAdvisor(advice);
		}
	}
	throw new UnknownAdviceTypeException(advice);
}
複製程式碼

wrap 的實現可以發現,若 advice 匹配了某個 adapter 將會建立一個 DefaultPointcutAdvisor 例項並返回;

public class DefaultPointcutAdvisor implements PointcutAdvisor, Ordered {

	private int order = Integer.MAX_VALUE;

	private Pointcut pointcut;
	
	private Advice advice;
	
	public DefaultPointcutAdvisor() {
	}
	
	public DefaultPointcutAdvisor(Advice advice) {
		this(Pointcut.TRUE, advice);
	}
	
	public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {
		this.pointcut = pointcut;
		this.advice = advice;
	}
}

/**
 * Canonical instance that matches everything.
 * 預設匹配所有的類及類下的所有方法
 */
Pointcut TRUE = new Pointcut() {

	public ClassFilter getClassFilter() {
		return ClassFilter.TRUE;
	}

	public MethodMatcher getMethodMatcher() {
		return MethodMatcher.TRUE;
	}

	public String toString() {
		return "Pointcut.TRUE";
	}
};
複製程式碼

DefaultPointcutAdvisor 的例項可以看出建立 advisor (切面) 的過程實際就是將 advice (通知) 和 pointcut (切入點) 繫結的過程;同時在 Spring AOP 預設的 pointcut 是攔截所有類下的所有方法。

簡單點說也就是當前切面將會攔截哪些類下的哪些方法,攔截過程中會採用哪些增強處理(前置通知,返回通知,異常通知)。

至此 advisor chain 的建立流程結束,其過程大概如下:

  • 遍歷 interceptor names (也就是 advice 通知)
  • 獲取 advice bean
  • 判斷 advice 是否匹配內建的 advisorAdapter, 匹配的話則建立 DefaultPointcutAdvisor (預設攔截所有類所有方法) 加入到連結串列中

建立目標代理物件

ProxyFactoryBean 類的名字及類結構,發現其實現介面 FactoryBean, 也就是說當其 getBean 的時候會呼叫方法 getObject, 原始碼如下:

public Object getObject() throws BeansException {
	// 預設單例
	return (this.singleton) ? getSingletonInstance() : newPrototypeInstance();
}

private Object getSingletonInstance() {
	if (this.singletonInstance == null) {
		// This object can configure the proxy directly if it's
		// being used as a singleton.
		this.singletonInstance = createAopProxy().getProxy();
	}
	return this.singletonInstance;
}

protected synchronized AopProxy createAopProxy() {
	if (!isActive) {
		activate();
	}
	
	return getAopProxyFactory().createAopProxy(this);
}
複製程式碼
public AopProxy createAopProxy(AdvisedSupport advisedSupport) throws AopConfigException {
	// 是否採用 cglib 代理
	boolean useCglib = advisedSupport.getOptimize() || advisedSupport.getProxyTargetClass() || advisedSupport.getProxiedInterfaces().length == 0;
	if (useCglib) {
		return CglibProxyFactory.createCglibProxy(advisedSupport);
	}
	else {
		// Depends on whether we have expose proxy or frozen or static ts
		return new JdkDynamicAopProxy(advisedSupport);
	}
}
複製程式碼
public Object getProxy(ClassLoader cl) {
	logger.debug("Creating JDK dynamic proxy");
	Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
	return Proxy.newProxyInstance(cl, proxiedInterfaces, this);
}
複製程式碼

ProxyFactoryBean 通過判斷 proxyTargetClass , interfaceNames 的配置去選擇採用 cglib 或者 jdk 來建立目標代理物件。

目標代理物件執行

上面簡單介紹了代理物件的建立,那麼在看下當我們呼叫目標方法的時候,代理是如何執行的,以 jdk 動態代理為例:

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

	TargetSource targetSource = advised.targetSource;
	Class targetClass = null;
	Object target = null;		
	
	try {
		// Try special rules for equals() method and implementation of the
		// Advised AOP configuration interface
		
		// Short-circuit expensive Method.equals() call, as Object.equals() isn't overloaded
		if (method.getDeclaringClass() == Object.class && "equals".equals(method.getName())) {
			// What if equals throws exception!?

			// This class implements the equals() method itself
			return new Boolean(equals(args[0]));
		}
		else if (Advised.class == method.getDeclaringClass()) {
			// Service invocations on ProxyConfig with the proxy config
			return AopProxyUtils.invokeJoinpointUsingReflection(this.advised, method, args);
		}
		
		Object retVal = null;
		
		// May be null. Get as late as possible to minimize the time we "own" the target,
		// in case it comes from a pool.
		// 目標實現類
		target = targetSource.getTarget();
		if (target != null) {
			targetClass = target.getClass();
		}
		
		if (this.advised.exposeProxy) {
			// Make invocation available if necessary
			oldProxy = AopContext.setCurrentProxy(proxy);
			setProxyContext = true;
		}
	
		// Get the interception chain for this method
		// 獲取目標類,執行方法的 interception chain
		List chain = this.advised.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
				this.advised, proxy, 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
			retVal = AopProxyUtils.invokeJoinpointUsingReflection(target, method, args);
		}
		else {
			
			invocation = new ReflectiveMethodInvocation(proxy, target,
								method, args, targetClass, chain);
									
			// Proceed to the joinpoint through the interceptor chain
			// 方法呼叫
			retVal = invocation.proceed();
		}
		
		// Massage return value if necessary
		if (retVal != null && retVal == target) {
			retVal = proxy;
		}
		return retVal;
	}
	finally {
	}
}

複製程式碼

首先我們看下如何獲取匹配當前 method 的攔截器, 參考 calculateInterceptorsAndDynamicInterceptionAdvice 的實現如下:

public static List calculateInterceptorsAndDynamicInterceptionAdvice(Advised config, Object proxy, Method method, Class targetClass) {
	// 用於儲存攔截器
	List interceptors = new ArrayList(config.getAdvisors().length);
	// 遍歷 advisor (切面)
	for (int i = 0; i < config.getAdvisors().length; i++) {
		Advisor advisor = config.getAdvisors()[i];
		if (advisor instanceof PointcutAdvisor) {
			// Add it conditionally
			PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
			// 判斷當前 target class 是否當前 pointcut
			if (pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
				// 獲取 advisor 對應的 method interceptor 
				MethodInterceptor interceptor = (MethodInterceptor) GlobalAdvisorAdapterRegistry.getInstance().getInterceptor(advisor);
				MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
				// 判斷當前 method 是否匹配 pointcut
				if (mm.matches(method, targetClass)) {
					if (mm.isRuntime()) {
						// Creating a new object instance in the getInterceptor() method
						// isn't a problem as we normally cache created chains
						interceptors.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm) );
					}
					else {							
						// 將攔截器加入連結串列中
						interceptors.add(interceptor);
					}
				}
			}
		}
		else if (advisor instanceof IntroductionAdvisor) {
			IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
			if (ia.getClassFilter().matches(targetClass)) {
				MethodInterceptor interceptor = (MethodInterceptor) GlobalAdvisorAdapterRegistry.getInstance().getInterceptor(advisor);
				interceptors.add(interceptor);
			}
		}
	}	// for
	return interceptors;
}	// calculateInterceptorsAndDynamicInterceptionAdvice

複製程式碼

我們在詳細看下如何查詢 advisor 匹配的攔截器呢,同樣與上文中 wrap 類似,如下:

public Interceptor getInterceptor(Advisor advisor) throws UnknownAdviceTypeException {
	Advice advice = advisor.getAdvice();
	if (advice instanceof Interceptor) {
		return (Interceptor) advice;
	}

	// 遍歷內建的 advisor adapter
	for (int i = 0; i < this.adapters.size(); i++) {
		AdvisorAdapter adapter = (AdvisorAdapter) this.adapters.get(i);
		// 是否匹配當前 advice
		if (adapter.supportsAdvice(advice)) {
			// 匹配的話返回 interceptor
			return adapter.getInterceptor(advisor);
		}
	}
	throw new UnknownAdviceTypeException(advisor.getAdvice());
}
複製程式碼

到目前為止,我們多次發現 AdvisorAdapter 的身影,下面我們看下其具體的實現, 以 BeforeAdviceAdapter 為例:

class BeforeAdviceAdapter implements AdvisorAdapter {

	/**
	 * @see org.springframework.aop.framework.adapter.AdvisorAdapter#supportsAdvice(java.lang.Object)
	 */
	public boolean supportsAdvice(Advice advice) {
		// 匹配 MethodBeforeAdvice
		return advice instanceof MethodBeforeAdvice;
	}

	/**
	 * @see org.springframework.aop.framework.adapter.AdvisorAdapter#getInterceptor(org.springframework.aop.Advisor)
	 */
	public Interceptor getInterceptor(Advisor advisor) {
		MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
		// 返回 MethodBeforeAdviceInterceptor
		return new MethodBeforeAdviceInterceptor(advice) ;
	}

}
複製程式碼

通過 AdvisorAdapter 很巧妙的將 Advice 和 Interceptor 結合起來,同時也會發現二者關係是一一對應的

下面在看下方法的真正呼叫過程, 由 ReflectiveMethodInvocation 的方法 proceed 實現:

public Object proceed() throws Throwable {
	//	We start with an index of -1 and increment early
	// 當執行到最後一個攔截器的時候將會呼叫目標方法
	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;
		if (dm.methodMatcher.matches(this.method, this.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);
	}
}
複製程式碼

下面具體看下 MethodInterceptor 的實現,分別是前置通知,返回通知,異常通知

public Object invoke(MethodInvocation mi) throws Throwable {
	// 目標方法前執行
	advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
	return mi.proceed();
}

public Object invoke(MethodInvocation mi) throws Throwable {
	// 先執行目標方法
	Object retval = mi.proceed();
	// 後置處理
	advice.afterReturning(retval, mi.getMethod(), mi.getArguments(), mi.getThis() );
	return retval;
}

public Object invoke(MethodInvocation mi) throws Throwable {
	try {
		// 執行目標方法
		return mi.proceed();
	}
	catch (Throwable t) {
		// 異常處理
		Method handlerMethod = getExceptionHandler(t);
		if (handlerMethod != null) {
			invokeHandlerMethod(mi, t, handlerMethod);
		}
		throw t;
	}
}
複製程式碼

至此 Spring AOP 代理物件的執行過程處理結束,其流程可大概總結如下:

  • 獲取當前目標方法的 interceptor chain
    1. 遍歷 advisor ,判斷當前目標類和目標方法是否匹配 advisor 對應的 ponitcut
    2. 通過匹配的 advisor 對應的 advice 匹配對應的 advisorAdapter , 進而獲取對應的 methodInterceptor
  • 執行攔截器
  • 執行目標方法

小結

Spring AOP 中的物件關係小結下:

  • Advisor : 翻譯是顧問,簡單理解其就是一個 Aspect (切面); 其內部繫結了對應的 Pointcut(切入點) 和 Advice(通知)。
  • Advisor Chain : 切面鏈,是一系列的切面的集合。
  • Advice : 通知,是對攔截方法的增強處理;在 1.0 版本中包含 BeforeAdivce, AfterReturningAdvice, ThrowsAdvice; 其面向的是使用者。
  • MethodInterceptor : 方法攔截器,是 Advice 的執行者; 與 Advice 是一一對應的。
  • AdvisorAdapter : Advice 的介面卡,是 Advice 和 MethodInterceptor 匹配的紐帶。
  • AdvisorAdapterRegistry : 是 AdvisorAdapter 的註冊中心,內建了 BeforeAdviceAdapter, AfterReturnAdviceAdapter, ThrowsAdviceAdapter; 用來將 Advice wrap 成一個 Advisor 並提供獲取 Advice 對應的 MethodInterceptor。

使用 Spring 1.0 版本時, 當我們自定義 Advice 時,可不可以同時支援多種 Advice 呢 ? 譬如:

public class UserAdvice implements MethodBeforeAdvice, AfterReturningAdvice {

    public void before(Method m, Object[] args, Object target) throws Throwable {
        System.out.println("do before advice ....");
    }

    public void afterReturning(Object returnValue, Method m, Object[] args, Object target) throws Throwable {
        System.out.println("do after returning ....");
    }
}
複製程式碼

那麼當測試後,您會發現只有 before 呼叫了,而 afterReturning 未呼叫了;這是為什麼呢 ? (好好看原始碼額)

相關文章