《spring技術內幕》讀書筆記3-AOP的實現

yeleits發表於2020-11-12

目錄

1. Demo

2. AOP的實現

2.1 生成代理

2.1.1 ProxyFactoryBean的UML

2.1.2 ProxyFactoryBean的getObject()

2.2 執行攔截

2.2.1 JdkDynamicAopProxy


1. Demo

首先是spring aop應用:

(1)準備工作:

package com.abc;

//工人介面
public interface Worker {
	void work();
}

//普通工人類
public class OrdinaryWorker implements Worker {
	@Override
	public void work() {
		System.out.println("* * * * * 搬磚 * * * * *");
	}
}

//資本家類
public class Capitalist {
	public void work() {
		System.out.println("$ ¥ $ ¥ $ ¥ $ ¥ $ ¥ $ ¥ $");
	}
}

//工人增強器(手動滑稽)
public class WorkerAdvisor extends AbstractPointcutAdvisor {

	@Override
	public Advice getAdvice() {
		return new WorkerAdvice();
	}

	@Override
	public Pointcut getPointcut() {
		String pattern = "com.abc.OrdinaryWorker.*";
		JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
		pointcut.setPattern(pattern);
		return pointcut;
	}

	static class WorkerAdvice implements AfterReturningAdvice, MethodBeforeAdvice {
		@Override
		public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
			System.out.println("21:00 - 滴,下班:沒有困難的工作,只有勇敢的打工人!");
		}
		@Override
		public void before(Method method, Object[] args, Object target) throws Throwable {
			System.out.println("9:00 - 滴,上班:早安打工人!");
		}
	}
}

//資本家增加器
public class CapitalistAdvisor extends AbstractPointcutAdvisor {

	@Override
	public Advice getAdvice() {
		return new CapitalistAdvice();
	}

	@Override
	public Pointcut getPointcut() {
		String pattern = "com.abc.Capitalist.*";
		JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
		pointcut.setPattern(pattern);
		return pointcut;
	}

	static class CapitalistAdvice implements AfterReturningAdvice {
		@Override
		public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
			System.out.println("(*^▽^*)又是愉快數錢的一天!");
		}
	}
}

(2)spring配置:

<bean id="workerAdvisor" class="com.abc.WorkerAdvisor"/>
<bean id="capitalistAdvisor" class="com.abc.CapitalistAdvisor"/>

<bean id="worker" class="com.abc.OrdinaryWorker"/>
<bean id="capitalist" class="com.abc.Capitalist"/>

<bean id="aopWorker" class="org.springframework.aop.framework.ProxyFactoryBean">
	<property name="target">
		<ref bean="worker"/>
	</property>
	<property name="interceptorNames">
		<list>
			<value>workerAdvisor</value>
			<value>capitalistAdvisor</value>
		</list>
	</property>
</bean>

<bean id="aopCapitalist" class="org.springframework.aop.framework.ProxyFactoryBean">
	<property name="target">
		<ref bean="capitalist"></ref>
	</property>
	<property name="interceptorNames">
		<list>
			<value>workerAdvisor</value>
			<value>capitalistAdvisor</value>
		</list>
	</property>
</bean>

(3)單測:

public class AopTest {
	@Test
	public void test() throws Exception {
		BeanFactory bf = new XmlBeanFactory(new ClassPathResource("mytest/aoptest.xml"));
		Object aopWorker = bf.getBean("aopWorker");
		Object aopCapitalist = bf.getBean("aopCapitalist");

		assert !(aopCapitalist instanceof Proxy);
		assert aopWorker instanceof Proxy;

		Capitalist capitalist = (Capitalist) aopCapitalist;
		capitalist.work();
		System.out.println("--------階級分割線--------");
		Worker worker = (Worker) aopWorker;
		worker.work();
	}
}

(4)執行結果:

$ ¥ $ ¥ $ ¥ $ ¥ $ ¥ $ ¥ $
(*^▽^*)又是愉快數錢的一天!
--------階級分割線--------
9:00 - 滴,上班:早安打工人!
* * * * * 搬磚 * * * * *
21:00 - 滴,下班:沒有困難的工作,只有勇敢的打工人!

思考:為什麼斷言能順利執行 —— aopCapitalist不是Proxy的例項,而aopWorker是Proxy的例項???且看下面分解……

 

2. AOP的實現

2.1 生成代理

spring是如何生成目標物件的代理,即解讀:Object aopWorker = bf.getBean("aopWorker");

2.1.1 ProxyFactoryBean的UML

"aopWorker"的型別是ProxyFactoryBean,UML圖如下:

根據1:ProxyFactoryBean是工廠Bean,參考【讀書筆記2-依賴注入】可知執行工廠bean的getObject方法

根據2:ProxyFactoryBean能感知自己所在的BeanFactory,參考《Spring技術內幕-第2版》2.5.7節

根據3:ProxyFactoryBean繼承AdvisedSupport,有幾個關鍵的成員變數:targetSource(被代理的目標物件)、interfaces(代理的介面)、advisors(增強器)

2.1.2 ProxyFactoryBean的getObject()

瞭解完PRoxyFactoryBean的getObject()邏輯後,單測的問題就有答案了。主要邏輯如下:

考慮到繼承關係,通過property標籤,既可以配置諸如:interceptorNames、targetName等來自ProxyFactoryBean的成員變數,也可以配置諸如targetSource、interfaces、advisors等來自AdvisedSupport的成員變數。

無論是基於jdk動態代理的JdkDynamicAopProxy,還是基於cglib的ObjenesisCglibAopProxy,都有1個成員變數:AdvisedSupport advised,記錄了代理目標的配置資訊。

2.2 執行攔截

spring生成目標物件的代理後,代理物件是如何執行被aop攔截的方法,即解讀:((Worker) aopWorker).work()。

從2.1節可知,AopProxy有2種實現:JdkDynamicAopProxy、ObjenesisCglibAopProxy,下面就分析一下JdkDynamicAopProxy的情況

2.2.1 JdkDynamicAopProxy

Java動態代理物件執行被代理的介面方法時,會執行InvocationHandler介面的invoke()方法。

JdkDynamicAopProxy實現了InvocationHandler介面,invoke()方法的主要邏輯如下:

從整體大局瞭解程式碼邏輯後,很自然的明白為什麼chain的泛型是Object,而不是Interceptor,因為元素還可能是InterceptorAndDynamicMethodMatcher型別。而isRuntime這種情況,應該是針對方法過載的情況,相同的方法簽名資訊,入參型別不同,需要執行時再判斷是否匹配。

spring對於不同advice轉成不同MethodInterceptor,採用了介面卡模式,還有多處應用了單例模式。

 

相關文章