前言
本篇文章是SpringAOP的原始碼學習分享,分為上下兩篇,本篇是對SpringAOP中切面織入業務bean時為業務bean生成動態代理物件的這一塊的原始碼學習。
正文
一. 示例工程搭建
通過引入Springboot來完成引入Spring的相關依賴,依賴項如下所示。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
使用的Springboot版本為2.4.1,對應的Spring版本為5.3.2。
首先自定義一個註解,用於在切點中定位到目標方法,如下所示。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyMethod {
}
然後定義業務介面和實現類,如下所示。
public interface IMyService {
void executeTask(String message);
void tempExecute(String message);
}
@Service
public class MyService implements IMyService {
@Override
@MyMethod
public void executeTask(String message) {
System.out.println(message);
}
@Override
public void tempExecute(String message) {
executeTask(message);
}
}
然後定義一個切面,已知切面 = 切點 + 通知
,在本示例工程中,切點是所有由@MyMethod
註解修飾的方法,並且選擇前置通知和後置通知,如下所示。
@Aspect
@Component
public class MyAspect {
@Pointcut("@annotation(com.learn.aop.aspect.MyMethod)")
public void myMethodPointcut() {}
@Before(value = "myMethodPointcut()")
public void commonBeforeMethod(JoinPoint joinPoint) {
System.out.println("Common before method.");
}
@After(value = "myMethodPointcut()")
public void commonAfterMethod(JoinPoint joinPoint) {
System.out.println("Common after method.");
}
}
自定義一個配置類,和上面所有類放在同一包路徑下,如下所示。
@ComponentScan
@EnableAspectJAutoProxy
public class MyConfig {}
最後編寫一個測試程式,如下所示。
public class MyTest {
public static void main(String[] args) {
ApplicationContext applicationContext
= new AnnotationConfigApplicationContext(MyConfig.class);
IMyService iMyService = applicationContext.getBean(IMyService.class);
iMyService.tempExecute("Real method execute.");
iMyService.executeTask("Real method execute.");
}
}
執行測試程式,列印如下。
可以看到執行executeTask()
方法時,切面邏輯生效了。
二. 時序圖
SpringAOP中動態代理物件的生成,可以分為兩個大的步驟。
- 步驟一:將作用於當前bean的通知獲取出來,得到通知鏈;
- 步驟二:基於通知鏈為當前bean生成AOP動態代理物件,並根據配置和目標bean決定是使用CGLIB動態代理還是JDK動態代理。
通知鏈可以表示如下。
List<Advisor> chain
Advisor
介面是SpringAOP中對通知的一個頂層抽象,其有兩個子介面,類圖如下所示。
PointcutAdvisor
是對切點相關的通知的抽象,可以將PointcutAdvisor
理解為對通知方法和切點的封裝,由於本文的示例工程中的切面中的通知全部是切點相關的通知,所以無特殊說明時,Advisor
均指PointcutAdvisor
,並且也可以不太嚴謹的將Advisor
稱為通知。
在理清了概念之後,下面給出時序圖,時序圖有兩張,一張是通知鏈的獲取時序圖,一張是AOP動態代理物件的生成時序圖,如下所示。
- 通知鏈的獲取時序圖。
- AOP動態代理物件的生成時序圖。
後續原始碼的分析可以結合上述時序圖進行理解。
三. SpringAOP動態代理物件生成時間點
在示例工程中,如果通過斷點除錯的方法,觀察iMyService欄位,可以發現其是一個動態代理物件,如下所示。
在bean生命週期中,bean的例項化,屬性注入和初始化都在AbstractAutowireCapableBeanFactory
的doCreateBean()
方法中,在該方法中會先呼叫createBeanInstance()
方法將bean例項化出來,然後呼叫populateBean()
方法為bean例項完成屬性注入,最後呼叫initializeBean()
方法來初始化bean。下面看一下initializeBean()
方法的實現。
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
......
if (mbd == null || !mbd.isSynthetic()) {
//呼叫BeanPostProcessors的postProcessBeforeInitialization()方法
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
......
if (mbd == null || !mbd.isSynthetic()) {
//呼叫BeanPostProcessors的postProcessAfterInitialization()方法
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
在初始化bean的時候,就會呼叫到BeanPostProcessors
(bean後置處理器)的postProcessBeforeInitialization()
和postProcessAfterInitialization()
方法,在BeanPostProcessors
的實現類AnnotationAwareAspectJAutoProxyCreator
的postProcessAfterInitialization()
方法中,就會為bean織入切面(為bean生成動態代理物件)。
下面給出AnnotationAwareAspectJAutoProxyCreator
的類圖。
四. 通知鏈的獲取
上一節中已知,在BeanPostProcessors
的實現類AnnotationAwareAspectJAutoProxyCreator
的postProcessAfterInitialization()
方法中,就會為bean織入切面(為bean生成動態代理物件),那麼就以AnnotationAwareAspectJAutoProxyCreator
的postProcessAfterInitialization()
方法為入口,開始分析原始碼。其實AnnotationAwareAspectJAutoProxyCreator
沒有對postProcessAfterInitialization()
方法做實現,那麼實際呼叫到的是AnnotationAwareAspectJAutoProxyCreator
父類AbstractAutoProxyCreator
的postProcessAfterInitialization()
方法,如下所示。
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
//如果bean是切面作用目標,就為bean生成動態代理物件
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
繼續看wrapIfNecessary()
方法的實現,如下所示。
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
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);
//為當前bean生成動態代理物件
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
//返回當前bean的動態代理物件
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
wrapIfNecessary()
方法會先將作用於當前bean的通知鏈獲取出來,然後再呼叫createProxy()
方法為當前bean建立動態代理物件,那麼本小節重點分析getAdvicesAndAdvisorsForBean()
方法的實現。getAdvicesAndAdvisorsForBean()
實現如下。
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
//將作用於當前bean的通知獲取出來,並且通知會被封裝成Advisor的實現類
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
繼續看findEligibleAdvisors()
方法,如下所示。
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
//找到容器中所有由@Aspect註解修飾的切面,並將切面中的每個通知方法都封裝成一個Advisor的實現類
List<Advisor> candidateAdvisors = findCandidateAdvisors();
//在candidateAdvisors中將作用於當前bean的Advisor獲取出來
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) {
//對Advisor進行排序
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
findEligibleAdvisors()
方法中會先呼叫到AnnotationAwareAspectJAutoProxyCreator
的findCandidateAdvisors()
方法,在findCandidateAdvisors()
方法中會再呼叫BeanFactoryAspectJAdvisorsBuilder
的buildAspectJAdvisors()
方法來遍歷當前容器中的每個由@Aspect
註解修飾的切面,然後將每個切面的通知封裝成Advisor
並返回,同時每遍歷一個切面,都會將這個切面的所有Advisor
快取,以便下次獲取時直接從快取獲取。
findEligibleAdvisors()
方法中在獲取到當前容器中的所有Advisor
後,會再呼叫findAdvisorsThatCanApply()
方法來找出能夠作用於當前bean的Advisor
,判斷依據就是根據Advisor
中的Pointcut
來判斷。
findEligibleAdvisors()
方法最後還會對作用於當前bean的所有Advisor
進行排序,這個後面再分析。所以findEligibleAdvisors()
方法執行完,就獲取到了能夠作用於當前bean的所有通知對應的Advisor
,也就獲取到了通知鏈。
最後再分析一下AnnotationAwareAspectJAutoProxyCreator
的findCandidateAdvisors()
方法中是如何獲取容器中所有通知以及是如何將每個通知封裝成Advisor
的。BeanFactoryAspectJAdvisorsBuilder
的buildAspectJAdvisors()
方法如下所示。
public List<Advisor> buildAspectJAdvisors() {
List<String> aspectNames = this.aspectBeanNames;
//aspectNames不為null表示獲取過切面bean的通知並把這些通知進行了快取,那麼直接從快取獲取通知
if (aspectNames == null) {
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new ArrayList<>();
aspectNames = new ArrayList<>();
//把容器中的bean的名字獲取出來
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
//遍歷每個bean
for (String beanName : beanNames) {
if (!isEligibleBean(beanName)) {
continue;
}
Class<?> beanType = this.beanFactory.getType(beanName, false);
if (beanType == null) {
continue;
}
//判斷bean是否是切面bean
if (this.advisorFactory.isAspect(beanType)) {
//把切面bean的名字新增到集合中,以便後續快取起來
aspectNames.add(beanName);
AspectMetadata amd = new AspectMetadata(beanType, beanName);
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
//呼叫到ReflectiveAspectJAdvisorFactory的getAdvisors()方法來獲取切面bean裡的通知
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
if (this.beanFactory.isSingleton(beanName)) {
//如果切面bean是單例,則快取切面bean的通知
this.advisorsCache.put(beanName, classAdvisors);
}
else {
//如果切面bean不是單例,則快取切面bean的工廠
//通過切面bean的工廠可以每次都生成切面bean的通知
this.aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);
}
else {
if (this.beanFactory.isSingleton(beanName)) {
throw new IllegalArgumentException("Bean with name '" + beanName +
"' is a singleton, but aspect instantiation model is not singleton");
}
MetadataAwareAspectInstanceFactory factory =
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
this.aspectFactoryCache.put(beanName, factory);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
}
this.aspectBeanNames = aspectNames;
//返回容器中的所有通知
return advisors;
}
}
}
//執行到這裡表示已經生成過通知並進行了快取
if (aspectNames.isEmpty()) {
return Collections.emptyList();
}
List<Advisor> advisors = new ArrayList<>();
for (String aspectName : aspectNames) {
//將每個切面bean的通知從快取中獲取出來並加到結果集合中
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
}
else {
//非單例切面bean就使用其對應的工廠新生成通知,然後也加入到結果集合中
MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
//返回容器中的所有通知
return advisors;
}
上面buildAspectJAdvisors()
方法中主要是Spring對切面bean的通知的一個快取策略,主要思想就是第一次獲取時會真實的將所有切面bean的通知獲取出來並生成Advisor
,然後快取起來,後續再獲取通知時就從快取中獲取。下面繼續深入分析一下切面bean的通知是如何被封裝成Advisor
的,實際的邏輯發生在ReflectiveAspectJAdvisorFactory
的getAdvisors()
方法中,如下所示。
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
//得到切面bean的Class物件
Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
//得到切面bean的名字
String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
validate(aspectClass);
MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
List<Advisor> advisors = new ArrayList<>();
//呼叫getAdvisorMethods()方法來把非切點方法獲取出來,並遍歷
for (Method method : getAdvisorMethods(aspectClass)) {
//先將通知上的切點構造成AspectJExpressionPointcut,然後再建立通知對應的Advisor
//建立出來的Advisor實際為InstantiationModelAwarePointcutAdvisorImpl
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
//當前method如果是通知方法,則將通知方法對應的Advisor新增到結果集合中
//如果不是通知方法,得到的Advisor會為null,就不會新增到結果集合中
if (advisor != null) {
advisors.add(advisor);
}
}
if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
advisors.add(0, instantiationAdvisor);
}
for (Field field : aspectClass.getDeclaredFields()) {
Advisor advisor = getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
}
return advisors;
}
至此獲取通知鏈的原始碼分析完畢。下面對獲取作用於某個bean的通知鏈步驟進行小節。
- 如果是第一次獲取通知鏈,那麼會遍歷容器中每個由
@Aspect
註解修飾的切面bean然後將其通知封裝成Advisor
並快取起來,如果不是第一次獲取,就直接從快取中將所有Advisor
獲取出來; - 然後篩選得到作用於當前bean的
Advisor
,並加入集合中; - 返回篩選得到的集合,作為後續建立AOP動態代理物件的通知鏈。
五. AOP動態代理物件的建立
已知在AbstractAutoProxyCreator
的wrapIfNecessary()
方法中會先呼叫getAdvicesAndAdvisorsForBean()
方法獲取作用於當前bean的通知鏈,那麼下一步就應該基於通知鏈為當前bean生成AOP動態代理物件,生成動態代理物件的邏輯在AbstractAutoProxyCreator
的createProxy()
方法中,如下所示。
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 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設定通知鏈
proxyFactory.addAdvisors(advisors);
//為ProxyFactory設定目標物件
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
//呼叫ProxyFactory的getProxy()方法建立動態代理物件
return proxyFactory.getProxy(getProxyClassLoader());
}
在createProxy()
方法中會先建立一個ProxyFactory
工廠,然後為ProxyFactory
工廠設定通知鏈和目標物件,後續的動態代理物件的建立就是由ProxyFactory
工廠來完成。ProxyFactory
工廠類圖如下所示。
所以ProxyFactory
其實是一個AdvisedSupport
。ProxyFactory
的getProxy()
方法如下所示。
public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
在ProxyFactory
的getProxy()
方法中會先呼叫到其父類ProxyCreatorSupport
中的createAopProxy()
方法,createAopProxy()
方法會有兩種返回值,一個是JdkDynamicAopProxy
,負責JDK動態代理物件的生成,另一個是CglibAopProxy
,負責CGLIB動態代理物件的生成,下面看一下ProxyCreatorSupport
的createAopProxy()
方法的實現。
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
//getAopProxyFactory()會返回一個AopProxyFactory
//AopProxyFactory的createAopProxy()會返回一個AopProxy
//根據不同的目標類和不同的配置,會最終決定AopProxy是JdkDynamicAopProxy還是CglibAopProxy
//建立AopProxy時還會將ProxyFactory自己傳入,所以建立出來的AopProxy也就持有了通知鏈和目標物件
return getAopProxyFactory().createAopProxy(this);
}
ProxyCreatorSupport
的createAopProxy()
方法中建立AopProxy
時會將ProxyFactory
傳入,所以建立出來的AopProxy
也就通過ProxyFactory
持有了通知鏈和目標物件。現在回到ProxyFactory
的getProxy()
方法,在拿到JdkDynamicAopProxy
或者CglibAopProxy
之後,就會呼叫其getProxy()
方法來生成動態代理物件,下面以JdkDynamicAopProxy
的getProxy()
方法為例進行說明,JdkDynamicAopProxy
的getProxy()
方法如下所示。
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
//呼叫Proxy的newProxyInstance()方法來生成動態代理物件
//proxiedInterfaces中有bean實現的介面
//JdkDynamicAopProxy自身是實現了InvocationHandler介面,所以這將JdkDynamicAopProxy傳到了newProxyInstance()方法中
return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
}
JdkDynamicAopProxy
的getProxy()
方法就是呼叫Proxy
的newProxyInstance()
方法來建立JDK動態代理物件。
至此SpringAOP中建立AOP動態代理物件的原始碼分析完畢。下面給出為bean基於通知鏈建立動態代理物件的步驟小節。
- 建立
ProxyFactory
,為ProxyFactory
設定通知鏈和目標物件,後續通過ProxyFactory
建立動態代理物件; - 通過
ProxyFactory
先建立AopProxy
,根據使用的動態代理方式的不同,建立出來的AopProxy
可以為JdkDynamicAopProxy
或者ObjenesisCglibAopProxy
,並且ProxyFactory
在建立AopProxy
時傳入了自身,所以建立出來的AopProxy
也就持有了通知鏈和目標物件; - 通過建立出來的
AopProxy
生成動態代理物件。
總結
Spring中有使用者自定義的切面以及Spring框架提供的切面,這些切面會在bean的生命週期呼叫到BeanPostProcessors
的postProcessAfterInitialization()
方法時織入bean,織入的形式就是為bean生成AOP動態代理物件。為bean生成動態代理物件前會先獲取到容器中所有能夠作用於這個bean的通知,這些通知會被封裝成Advisor
的實現類並加入到集合中,可以稱這個Advisor
的集合為通知鏈,獲取到通知鏈後,會建立一個ProxyFactory
工廠來幫助建立動態代理物件,建立前會先通過ProxyFactory
建立AopProxy
,根據使用的動態代理方式的不同,建立出來的AopProxy
可以為JdkDynamicAopProxy
或者ObjenesisCglibAopProxy
,並且ProxyFactory
在建立AopProxy
時傳入了自身,所以建立出來的AopProxy
也就持有了通知鏈和目標物件,最後就是通過AopProxy
將實際的動態代理物件生成出來。