Spring IOC---AOP代理物件生成的時機
技術標籤:# Spring原始碼解析springjavaaopioc
文章目錄
1.概述
Spring AOP可以採用註解或者xml配置的方式實現,那麼在spring的生命週期當中,是在什麼時候生成的代理物件呢?本文就AOP代理物件生成的時機進行介紹。不清楚spring生命週期的讀者可以先閱讀另一篇部落格《Spring IOC—Bean的生命週期》。
2.前置知識
- BeanPostProcessor介面的作用
簡單的講就是在一個物件初始化的前後做一些事情,裡面有兩個方法,一個是postProcessBeforeInitialization,例項化、依賴注入完畢,在呼叫顯示的初始化之前完成一些定製的初始化任務。另一個是postProcessAfterInitialization,例項化、依賴注入、初始化完畢時執行
3.Spring AOP代理物件生成的時機
可以分成兩類,提前和非提前生成代理物件。
下面分別介紹這兩類代理物件生成的時機,以及說明為什麼會有這兩種情況。
3.1非提前生成代理物件
下面給個例子,通過除錯程式碼來說明代理的時機,讀者可以跟著一邊除錯一邊閱讀。
//被代理類 A
package hdu.gongsenlin.aoptest.dao;
@Component
public class A {
public void f(){
System.out.println("AAAAA");
}
}
//切面類
package hdu.gongsenlin. aoptest.aop;
@Aspect
@Component
public class Aop {
@Pointcut("execution(* hdu.gongsenlin.aoptest.dao..*.*(..))")
private void pointcut(){}
@After("pointcut()")
public void advice(){
System.out.println("之後增強------------");
}
}
//配置類
package hdu. gongsenlin.aoptest;
@Configuration
@ComponentScan("hdu.gongsenlin.aoptest")
@EnableAspectJAutoProxy
public class Appconfig {
}
//啟動類
package hdu.gongsenlin.aoptest;
public class AoptestApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Appconfig.class);
A a = ac.getBean(A.class);
a.f();
}
}
上面的程式碼AOP會作用在A物件上,也就是生成A的代理物件。
這一過程發生在A的生命週期當中,將程式碼定位到AbstractAutowireCapableBeanFactory#doCreateBean方法
瞭解springBean的生命週期的讀者都應該清楚,Bean生命週期中有一步是屬性填充 population,在屬性填充之後會執行initializeBean
initializeBean方法中會執行applyBeanPostProcessorsAfterInitialization方法
applyBeanPostProcessorsAfterInitialization具體的程式碼如下:
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
邏輯比較簡單,就是遍歷所有實現了BeanPostProcessor介面的類,這裡一共有這7個,一個一個去執行postProcessAfterInitialization方法。其中AnnotationAwareAspectJAutoProxyCreator就是會實現AOP動態代理,然後返回代理物件。
AnnotationAwareAspectJAutoProxyCreator中的postProcessAfterInitialization程式碼如下
根據bean型別和名字建立快取key,判斷earlyProxyReferences提前動態代理的集合當中存不存在這個快取key,若存在則說明已經進行過動態代理了,則不再進行動態代理,而本例子中,很明顯是沒有執行提前動態代理的,所以會執行wrapIfNecessary方法進行構建動態代理物件,本文僅介紹執行的時機,具體的動態代理的實現過程暫時先不考慮。
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return this.wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
所以非提前生成代理物件 是在屬性填充populateBean完成之後,執行了initializeBean方法的時候進行的動態代理。
3.2 提前生成代理物件
這種情況比較的複雜,涉及到了迴圈依賴的問題,對於Bean的迴圈依賴不瞭解的讀者,可以先閱讀《Spring IOC—迴圈依賴》這篇部落格再接著閱讀。
同樣的以舉例的方式說明
//切面類 不變
package hdu.gongsenlin.aoptest.aop;
@Aspect
@Component
public class Aop {
@Pointcut("execution(* hdu.gongsenlin.aoptest.dao..*.*(..))")
private void pointcut(){}
@After("pointcut()")
public void advice(){
System.out.println("之後增強------------");
}
}
//啟動類 不變
public class AoptestApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Appconfig.class);
A a = ac.getBean(A.class);
a.f();
}
}
//配置類 不變
@Configuration
@ComponentScan("hdu.gongsenlin.aoptest")
@EnableAspectJAutoProxy
public class Appconfig {
}
//被代理物件 A 依賴了 普通物件B
package hdu.gongsenlin.aoptest.dao;
@Component
public class A {
@Autowired
B b;
public void f(){
System.out.println("AAAAA");
}
}
// 普通物件B 依賴了 A
package hdu.gongsenlin.aoptest.service;
@Component
public class B {
@Autowired
A a;
public void f(){
System.out.println("BBBBBBBB");
}
}
此時給的例子中,A和B相互依賴,形成了迴圈依賴,不同的是A需要被動態代理,而B不需要。
繼續將程式碼定位到建立A的時候,還沒有執行屬性填充populateBean的位置,如下:
因為涉及到了迴圈依賴,所以準備三個框框,來代表迴圈依賴需要用到的三個集合。
至於這三個集合的作用 在迴圈依賴的部落格中已經介紹了,這裡就不再贅述。
在上面程式碼執行之前,會將a的工廠物件放入到singletonFactories,此時三個集合的情況如下:
之後執行構建A的populateBean進行屬性填充,發現A依賴於B,而B又不存在於這三個集合當中,所以會遞迴的建立B,呼叫doCreateBean來構建B物件,和A相同,在執行populateBean填充屬性之前,會將b的工廠物件放入到singletonFactories當中,此時三個集合的情況如下:
接著會執行構建B的populateBean進行屬性填充,此時B依賴於A,所以會呼叫doGetBean的去找A物件
然後呼叫getSingleton,該方法是解決迴圈依賴的關鍵,程式碼如下:
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Quick check for existing instance without full singleton lock
Object singletonObject = this.singletonObjects.get(beanName);//先從單例池中去獲取
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {//如果單例池中沒有,並且當前的bean正在建立
singletonObject = this.earlySingletonObjects.get(beanName);// 看看有沒有提前暴露的不完整的Bean
if (singletonObject == null && allowEarlyReference) {// 如果還沒有 且允許提前建立
synchronized (this.singletonObjects) {
// Consistent creation of early reference within full singleton lock
singletonObject = this.singletonObjects.get(beanName);// 再檢查一次 singletonObjects 雙重檢查
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);// 再檢查一次 earlySingletonObjects 雙重檢查
if (singletonObject == null) {//若都還沒有
// 則獲取beanName對應的單例工廠
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//通過工廠建立一個物件
singletonObject = singletonFactory.getObject();
//將這個例項化的物件放入到earlySingletonObjects
this.earlySingletonObjects.put(beanName, singletonObject);
//從單例工廠中移除 這個工廠。
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
一級快取和二級快取中沒有找到A物件,而三級快取singletonFactories當中有A的工廠物件,所以會呼叫
singletonFactory.getObject()來獲得A物件。
這是之前添加工廠物件到singletonFactories的程式碼,所以其實執行getObject()也就是執行了getEarlyBeanReference方法
getEarlyBeanReference的方法程式碼如下:
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
此時又出現了AnnotationAwareAspectJAutoProxyCreator,這裡會執行它的getEarlyBeanReference方法
基於型別和名字 建立快取key,將其放入到earlyProxyReferences集合當中,用於表示進行了提前的動態代理。呼叫wrapIfNecessary來構建動態代理物件。
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
return this.wrapIfNecessary(bean, beanName, cacheKey);
}
此時獲得到了被代理後的A物件,三個集合的結果如下:
之後doCreateBean 構建B的過程結束了,獲得了B物件
之後會將這個物件,新增到singletonObjects集合當中,三個集合的結果如下:
B物件構建完成了,那麼此時A就可以完成它的填充了,所以走完剩下的邏輯之後,三個集合的結果如下:
綜上提前動態代理,其實是在依賴注入的時候,也就是在populateBean屬性填充方法內完成的。
4. 為什麼需要兩個AOP動態代理的時機
沿用3.2的例子,假設動態代理不提前,那麼在構建B物件進行屬性填充的時候,填充的A物件是還沒有進行動態代理的A。
此時B就完成了它的生命週期到了單例池當中,而後A執行完屬性填充之後,再進行動態代理,生成一個被代理的A物件。放入到單例池當中。
此時B中的A和單例池中的被代理的A物件不是同一個物件,這就造成了問題。