天天看點

源碼解析Spring Bean的生命周期一、生成bean的模型對象BeanDefinition二、建立Bean三、增強bean功能四、單例bean的銷毀五、總結六、擴充章節:Bean後置處理器

源碼解析Spring Bean的生命周期

  • 一、生成bean的模型對象BeanDefinition
  • 二、建立Bean
    • 2.1 執行個體化之前:加載類
    • 2.2 執行個體化之前:可以提前傳回bean(很少用到)
    • 2.3 推斷構造器并執行個體化
    • 2.4 BeanDefinition的後置處理(很少用到)
    • 2.5 填充屬性(依賴注入)
  • 三、增強bean功能
    • 3.1 擴充點:Aware接口
    • 3.2、擴充點:初始化方法之前
    • 3.3 擴充點:初始化方法
    • 3.4 擴充點:初始化方法之後
  • 四、單例bean的銷毀
  • 五、總結
  • 六、擴充章節:Bean後置處理器
    • 6.1 Spring中的Bean後置處理器
      • 6.1.1 典型例子之一:CommonAnnotationBeanPostProcessor
      • 6.1.2 典型例子之二:AnnotationAwareAspectJAutoProxyCreator
    • 6.2 BeanPostProcess的順序

聲明:下文中涉及的Spring源碼都是Spring 5.3.7版本

Spring中用的最多的是單例bean,單例bean的建立、使用、銷毀都是由Spring容器管理的,對于原型bean,spring隻負責建立,在建立結束之後就不會再管了,銷毀或釋放資源都是要由開發者自己控制,是以我們經常說的"spring bean的生命周期"實際上指的是單例bean的生命周期。對于單例bean和原型bean以外類型的bean,由于使用的場景比較少,這裡也不進行講述。

Spring bean說到底也就是一個java對象,一個java對象的生命周期是什麼樣子的?首先通過構造器将對象執行個體化出來,如果構造器執行個體化的時候沒有給屬性指派,在執行個體化之後還可以通過set方法給屬性指派,這之後就可以使用這個對象,使用完成之後被jvm回收掉。Spring bean也是java對象,是以和java對象一樣spring bean的生命周期也離不開執行個體化 -> 使用 -> 最後銷毀的過程,不過Spring作為一個功能全面、強大的架構,可以對bean的生命周期進行更細粒度的控制。除了執行個體化之外,還可以處理bean的依賴關系(依賴注入);可以對bean進行各種功能增強;也允許使用者自定義一些初始化操作;對于單例bean來說,spring會将其儲存在容器中,這樣暫時使用完的bean不會被垃圾回收,下次使用的時候不需要再建立,而是直接擷取。總之,Spring對bean的建立、使用、銷毀進行了全面的接管,這也是IoC(控制反轉)的具體展現。

詳細來說我們可以把bean的生命周期分為如下幾個階段:

  • 1、掃描包并生成bean的模組化對象BeanDefinition
  • 2、建立bean(包括bean的執行個體化和依賴注入)
  • 3、增強bean(通過各種擴充點進行功能增強)
  • 4、使用bean
  • 5、關閉Spring容器的時候銷毀bean

按照這樣的劃分我們來看Spring bean完整的生命周期:

一、生成bean的模型對象BeanDefinition

BeanDefinitio即bean的模組化器或者bean的定義器,它儲存了一個Bean的所有資訊,這些資訊包括bean的屬性、構造方法參數、顯示依賴的Bean的名稱(即@DependsOn注解中指定的bean名稱)、是否單例、是否延遲加載、初始化和銷毀方法等各種資訊,Spring最終會根據BeanDefinition來建立對象。那BeanDefinition是什麼時候建立的呢?

Spring容器在啟動的時候會去掃描指定的包,識别出哪些類需要由spring接管,每掃描到一個類會生成一個MetadataReader對象,在這個對象中通過ASM架構(Java位元組碼操控架構,可以友善的解析出類中的所有元素:類名、方法、屬性等,cglib動态代理底層也使用了ASM架構,其性能要遠好于java反射)解析class檔案,得到類的中繼資料資訊和注解資訊,通過這些資訊就可以建立BeanDefinition對象了。這裡要注意,掃描過程中擷取的這些類本身沒有進行類加載,是以這個時候jvm中還沒有這些類的Class對象。

思考:
java中類的Class對象已經包含了類的各種資訊,為什麼還需要有一個專門的BeanDefinition來儲存類的資訊?

答案:
Class對象确實儲存了一個類的所有資訊,但是Spring執行個體化一個bean需要更多資訊,比如是否懶加載,bean的scope,bean的依賴等等,這些是Class對象無法提供的,是以Spring專門設計了這樣一個類來存儲定義一個bean所需要的各種資訊。
           

二、建立Bean

擷取到bean的BeanDefinition之後就可以根據BeanDefinition建立bean了,Spring容器會按照字典序來建立非懶加載的這些Bean。

如果調試過這個建立的步驟,會發現Spring最終會調用到AbstractAutowireCapableBeanFactory類的createBean()方法,createBean()方法又會調用doCreateBean()方法,doCreateBean()還會調用initializeBean()方法,這三個方法執行結束,一個bean就真正意義的建立完成了,是以這三個方法非常重要,從這三個方法就可以了解建立bean和增強bean功能的整個過程。這裡先貼出這三個方法的源碼,省略了其中部分對了解生命周期不是特别重要的代碼,比如異常處理之類的,然後對生命周期中重要的步驟進行了注釋并标号,後面會對這些步驟詳細講解,可以對照來看。

AbstractAutowireCapableBeanFactory中第一個重要的方法——createBean:

// AbstractAutowireCapableBeanFactory中的createBean方法
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {

	RootBeanDefinition mbdToUse = mbd;
    // 1、進行類加載
	Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
	...
    // 2、如果使用者實作了InstantiationAwareBeanPostProcessor,則直接傳回bean,并結束剩餘建立流程(很少用到)
	Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
	if (bean != null) {
		return bean;
	}
	
    // 調用doCreateBean方法,這是上面提到的第二個重要的方法
	Object beanInstance = doCreateBean(beanName, mbdToUse, args);
	return beanInstance;
}
           

AbstractAutowireCapableBeanFactory中第二個重要的方法——doCreateBean:

// AbstractAutowireCapableBeanFactory中的doCreateBean方法
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {

	BeanWrapper instanceWrapper = null;
	if (mbd.isSingleton()) {
		instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
	}
	if (instanceWrapper == null) {
	    // 3、推斷構造器并執行個體化bean
		instanceWrapper = createBeanInstance(beanName, mbd, args);
	}
	Object bean = instanceWrapper.getWrappedInstance();
	Class<?> beanType = instanceWrapper.getWrappedClass();
	if (beanType != NullBean.class) {
		mbd.resolvedTargetType = beanType;
	}

	// Allow post-processors to modify the merged bean definition.
	if (!mbd.postProcessed) {
	    // 4、BeanDefinition的後置處理器,可以更改bean的屬性等(很少用到)
		applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
		mbd.postProcessed = true;
	}
    ... 
	Object exposedObject = bean;
	
    // 5、處理依賴關系(依賴注入)
	populateBean(beanName, mbd, instanceWrapper);
	// 調用initializeBean方法,這是上面提到的第三個重要的方法
	exposedObject = initializeBean(beanName, exposedObject, mbd);
	
	...
	return exposedObject;
}
           

AbstractAutowireCapableBeanFactory中第三個重要的方法——initializeBean:

// AbstractAutowireCapableBeanFactory中的initializeBean方法
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
	if (System.getSecurityManager() != null) {
		AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
			invokeAwareMethods(beanName, bean);
			return null;
		}, getAccessControlContext());
	} else {
	    // 6、執行aware接口(第一處擴充點)
		invokeAwareMethods(beanName, bean);
	}

	Object wrappedBean = bean;
	if (mbd == null || !mbd.isSynthetic()) {
	    // 7、執行bean後置處理器的前方法(第二處擴充點)
		wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
	}

    // 8、執行初始化方法(第三處擴充點)
	invokeInitMethods(beanName, wrappedBean, mbd);
	
	if (mbd == null || !mbd.isSynthetic()) {
	    //  9、執行bean後置處理器的後方法(第四處擴充點)
		wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
	}
	return wrappedBean;
}
           

上面标号的注釋就是建立bean和增強bean的九個步驟,其中第二和第四步很少用到,剩餘的七個都很重要,下面具體來看。

2.1 執行個體化之前:加載類

在前面有提到,掃描類的時候并沒有進行類加載,但是執行個體化一個對象就必須要先進行類加載,是以AbstractAutowireCapableBeanFactory類的createBean()方法中做的第一件事就是進行類加載,對應上面源碼中标号為1的注釋的位置:

// 1、resolveBeanClass方法進行類加載
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
           

2.2 執行個體化之前:可以提前傳回bean(很少用到)

Spring允許不按照正常流程來建立一個Bean,開發者可以實作InstantiationAwareBeanPostProcessor接口,并在這個接口的postProcessBeforeInstantiation方法中傳回一個Bean對象,如果這個對象不為空,就會結束剩餘的建立流程,提前傳回這個bean,這個bean如果是單例的話依然會放入到單例池中,隻是不會進行依賴注入和功能增強。

// 2、提前傳回bean,并結束生命周期
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
	return bean;
}
           

不過這種用法很少會用到,基本可以忽略這種用法。

2.3 推斷構造器并執行個體化

執行個體化對象當然需要用到構造器,Spring可以推斷構造器,選擇合适的構造器來進行bean的執行個體化。

// 3、推斷構造器并執行個體化bean
instanceWrapper = createBeanInstance(beanName, mbd, args);
           

推斷構造方法其實沒那麼複雜:

如果隻有一個無參的構造方法,那麼執行個體化就隻能使用這個構造方法了。

如果有多個構造方法,其中一個是無參構造方法,其餘的是有參構造方法,這些構造方法都沒有@AutoWired注解,也沒有在xml檔案中進行設定,那麼spring會預設選擇無參構造方法。

如果有多個構造器,都是有參構造器,這些構造器上沒有@AutoWired注解,也沒有在xml檔案中進行設定,spring啟動時會報錯。

如果隻有一個有參構造器,這個構造器上沒有@AutoWired注解,也沒有在xml檔案中進行設定,spring會看這個有參構造器的參數(可能是其它bean或者常量)是不是都能擷取到,如果不能都擷取到就會報錯,如果都能擷取到就會用這個構造器。

其它情況就看使用者指定了哪個構造器,指定構造器有兩種方式,一是通過@Autowired注解,二是通過xml中的<constructor-arg>标簽來指定構造器參數,spring會根據參數來選擇構造器,如果能找到一個合适的構造器,spring就會使用使用者指定的構造器。但如果使用者指定了多個構造器,比如在多個構造器上使用@Autowired注解或者通過xml标簽不能推斷出一個唯一的構造器,spring也會報錯。

可能情況還不止上面這些,但是整體思路就是看spring能否找到一個不存在歧義的構造器(唯一的構造器或使用者唯一指定的構造器),如果能找到,那麼就使用這個構造器,如果找不到就看使用者是否允許spring自動選擇構造器,如果不允許就報錯,如果允許就看spring自動選擇的這個構造的參數是否都能擷取到,如果都能擷取到就會使用這個構造器,如果不能都擷取到就會報錯。

2.4 BeanDefinition的後置處理(很少用到)

在BeanDefinition的後置進行中可以修改BeanDefinition,但是此時執行個體對象已經生成好了,是以想通過修改beanClass來改變bean對象已經沒用了,但是這個時候bean隻是執行個體化出來,還沒進行依賴注入,是以可以修改PropertyValues:

if (!mbd.postProcessed) {
	try {
	    // 4、BeanDefinition的後置處理器,可以更改bean的屬性等
		applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
	}
	catch (Throwable ex) {
	    ...
	}
	mbd.postProcessed = true;
}
           

這種用法也很少會用到,基本可以忽略。

2.5 填充屬性(依賴注入)

populateBean()方法進行了屬性填充,所謂屬性填充其實就是依賴注入,spring會先去容器中尋找依賴的bean,如果找到了就直接注入,如果沒有找到就會去建立依賴的bean,是以這個方法是一個遞歸的入口,等依賴的bean建立完之後,會接着進行目前bean的屬性注入。這裡就涉及到spring bean循環依賴的問題,這是一個比較複雜的問題,用一個單獨的篇章來講,詳見:源碼解析Spring循環依賴

// 5、處理依賴關系(依賴注入)
populateBean(beanName, mbd, instanceWrapper);
           

上面建立bean的步驟細分有很多步,但如果粗略劃分,其實最重要的隻有兩步:一是執行個體化,二是屬性填充,這兩步也是建立一個java對象的過程。

三、增強bean功能

在屬性填充結束之後,從java的角度來說這個bean已經是一個完整的java對象,可以使用了。但是spring架構更進一步,通過各種擴充點來對bean的功能進行增強,這些擴充點都是使用者可選的,并不是必須的。

3.1 擴充點:Aware接口

第一處擴充點就是Aware接口,Aware單詞有"發現…/對…有興趣"的含義,從名字就可以看出來aware接口讓使用者能夠從spring容器中擷取一些有用的、感興趣的資訊或對象。Aware接口的執行對應上面源碼中标号為6的注釋的位置:

// 6、執行aware接口(第一處擴充點)
invokeAwareMethods(beanName, bean);
           

Aware接口有很多,這裡列舉幾個常見的:

  • BeanNameAware用于擷取實作這個接口的bean的名稱;
  • BeanFactoryAware用于擷取beanFactory對象,beanFactory對象其實就是spring容器。
  • ApplicationContextAware用于擷取aplicationContext對象(應用上下文),aplicationContext實作了beanFactory,是以aplicationContext也是spring容器。

beanFactory和aplicationContext是spring容器不同程度上的抽象,BeanFactory接口定義了容器應該具有的基礎行為,隻要一個類實作了BeanFactory接口,我們就可以稱它為容器。ApplicationContext接口組合了BeanFactory接口和一些其它接口,是以ApplicationContext接口擴充了容器的行為,我們稱實作了BeanFactory接口的類為容器,那麼實作了ApplicationContext接口的類也是容器,隻是這個容器會具有更多的行為,使用起來會更加友善。

一個正在運作的spring應用隻會有一個容器,但是jvm中可以同時有beanFactory和aplicationContext對象,這是不沖突的,這種情況下beanFactory對象是aplicationContext對象的一個屬性,相當于aplicationContext對象靜态代理了beanFactory對象,這屬于實作上的細節。正因為jvm中可以同時有beanFactory和aplicationContext對象,是以我們可以在一個bean上同時實作BeanFactoryAware和ApplicationContextAware接口,雖然這麼做沒有意義。

@Component
public class BeanC implements BeanFactoryAware, ApplicationContextAware {
    private BeanFactory beanFactory;
    private ApplicationContext applicationContext;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
           

BeanFactoryAware和ApplicationContextAware接口可以讓我們擷取到spring容器,這其實是實作依賴查找的關鍵,所謂依賴查找就是事先不進行bean的注入,而是在需要使用的時候從容器中查找,既然要從容器中查找,那必然要先擷取容器對象,這兩個接口可以讓我們擷取容器對象。

3.2、擴充點:初始化方法之前

從通用的設計角度來說,一個架構可以允許使用者在對象執行個體化完成之後執行一些操作,這些操作統一放在一個方法中,叫做初始化方法。Spring也一樣,當spring将一個bean執行個體化完成之後,就會執行這個初始化方法。但是在這個初始化方法的前後還可以增加擴充點,做到全方位的可擴充,這就是bean後置處理器(BeanPostProcessor)的前方法和後方法,前方法就是初始化之前執行的方法,後方法是初始化之後執行的方法。

前方法的執行對應上面源碼中标号為7的注釋的位置:

// 7、執行bean後置處理器的前方法(第二處擴充點)
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
           

由于Bean後置處理器是比較重要的一塊功能,涉及的内容也很多,是以用一個單獨的章節講解,詳細内容請跳轉到第六章。

這裡面有一個特别要關注的點是@PostConstruct注解,這個注解用于标注一個方法,這個方法會在在bean執行個體化結束之後執行一些操作,和初始化方法的作用是一樣的,但是我們知道**@PostConstruct注解标注的方法會在初始化方法之前執行**,這可以在Spring實作這個注解的方式中找到原因。細心的小夥伴會發現這個注解是javax包中的注解,并不是Spring自己的注解,Spring使用了CommonAnnotationBeanPostProcessor這個bean後置處理器來處理javax包中常用的注解,@PostConstruct注解就是其中之一,而且是在前方法中去執行這個注解标注的方法,這就是為什麼@PostConstruct注解标注的方法會在初始化方法之前執行的原因,在第六章有源碼解析,可以跳轉檢視。

與之相對的是@PreDestroy注解,這個注解也是javax包中的注解,也通過CommonAnnotationBeanPostProcessor的前方法來處理,但是和@PostConstruct不一樣,@PostConstruct标注的方法會在前方法中直接執行,而@PreDestroy不會直接執行,而是暫時儲存起來等到bean銷毀之前執行。

3.3 擴充點:初始化方法

Spring允許使用者自定義一些操作,這些操作會在bean執行個體化完成之後執行,這些操作放在一個統一的方法中叫做初始化方法。

初始化方法有多種指定方式,這裡列舉最為常用的兩種,第一種是實作InitializingBean接口,在這個接口的afterPropertiesSet()方法中定義初始化操作;第二種是在xml配置檔案中通過init-method标簽指定初始化方法,現在用xml配置檔案的方式已經比較少了。這兩種方式可以共存,會先執行在afterPropertiesSet()方法,然後執行init-method标簽指定初始化方法,但是一般不會有人同時混用這兩種方式。

初始化方法的執行對應上面源碼中标号為8的注釋的位置:

// 8、執行初始化方法(第三處擴充點)
invokeInitMethods(beanName, wrappedBean, mbd);
           

3.4 擴充點:初始化方法之後

初始化方法之後的擴充點就是bean後置處理器的後方法,這裡典型的應用就是Spring AOP的功能實作,Spring AOP底層用的是動态代理,代理對象就是在Bean後置處理器的後方法中進行生成的,詳細内容請跳轉到第六章。

後方法的執行對應于上面源碼中标号為9的注釋的位置:

// 9、執行bean後置處理器的後方法(第四處擴充點)
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
           

這一步執行完之後一個bean就真正意義的建立完成,如果是單例bean還會加入到單例池中,之後就可以真正使用了,直到最後在容器關閉的時候被銷毀。

四、單例bean的銷毀

容器銷毀的時候會進行單例bean的銷毀。可以想象一下,如果所有單例bean都沒有定義銷毀方法,那麼容器隻需要直接清空儲存單例bean的緩存就可以了。但如果有一些單例bean定義了銷毀方法,就需要找出這些單例bean,執行銷毀方法,執行完之後再清空單例bean的緩存。是以先要找出那些定義了銷毀方法的bean,然後執行這些銷毀方法。有多種方式來定義銷毀方法,這裡列舉最為常用的三種方式

  • a、使用@PreDestroy注解,在這個注解标注的方法中定義銷毀操作
  • b、實作DisposableBean接口,在destroy()方法中定義銷毀操作
  • c、在xml配置檔案中使用destroy-method标簽指定銷毀方法

Spring使用了擴充卡模式來統一這些銷毀方法的處理方式,這個擴充卡就是DisposableBeanAdapter,最終會在這個擴充卡中的destroy方法中來執行具體的銷毀方法。

// DisposableBeanAdapter中的destroy方法
@Override
public void destroy() {
	if (!CollectionUtils.isEmpty(this.beanPostProcessors)) {
	    // 執行@PreDestroy注解标注的方法
		for (DestructionAwareBeanPostProcessor processor : this.beanPostProcessors) {
			processor.postProcessBeforeDestruction(this.bean, this.beanName);
		}
	}

	if (this.invokeDisposableBean) {
		try {
			if (System.getSecurityManager() != null) {
				AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
					((DisposableBean) this.bean).destroy();
					return null;
				}, this.acc);
			}
			else {
			    // 執行DisposableBean接口的destroy()方法
				((DisposableBean) this.bean).destroy();
			}
		}
		catch (Throwable ex) {
			...
		}
	}

    // 執行xml标簽中指定的銷毀方法或者其他方式指定的銷毀方法
	if (this.destroyMethod != null) {
		invokeCustomDestroyMethod(this.destroyMethod);
	} else if (this.destroyMethodName != null) {
		Method methodToInvoke = determineDestroyMethod(this.destroyMethodName);
		if (methodToInvoke != null) {
			invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(methodToInvoke));
		}
	}
}
           

上面提到的三種銷毀方法可以同時存在,先執行@PreDestroy标注的方法,然後執行DisposableBean接口的destroy()方法,最後執行xml中destry-method标簽指定的銷毀方法。當然沒有必要同時混用這幾中方法。

五、總結

Spring Bean是java對象的一種特例,是以和java對象一樣需要執行個體化和填充屬性,但是Spring作為架構還可以在bean執行個體化并填充屬性之後進行各種功能增強。執行個體化、填充屬性、增強功能這是Spring Bean生命周期最為主要的三個階段,如果簡略劃分甚至可以把這三個階段作為Spring Bean的生命周期。當然如果進一步細分就是如下五個階段:

  • 1、掃描包并生成bean的模組化對象BeanDefinition
  • 2、建立bean:這個階段中spring推斷bean的構造器進行執行個體化并完成依賴注入(執行個體化+填充屬性)
  • 3、增強bean功能:包括Aware接口增強、bean後置處理器前後方法、初始化方法
  • 4、使用bean
  • 5、關閉spring容器的時候銷毀bean

最後來看一看Spring Bean生命周期中有一些成對的接口或者注解,比如:

  • @PostConstruct和@PreDestroy
  • InitializingBean接口的afterPropertiesSet()方法和DisposableBean接口的destroy()方法
  • xml配置檔案中的init-method和destroy-method标簽

這些成對的接口或注解用于定義一些初始化操作和銷毀操作,在使用上都是類似的,執行順序上也是類似的,初始化時先執行@PostConstruct注解标注的方法,然後是afterPropertiesSet()方法,然後是init-method指定的方法;銷毀時先執行@PreDestroy注解标注的方法,然後是destroy()方法,然後是destroy-method标簽指定的方法。

六、擴充章節:Bean後置處理器

Spring允許一個bean執行個體化完成之後執行一些操作,這些操作統一放在一個方法中,叫做初始化方法,在這個初始化方法的前後還可以增加功能擴充點,這兩個擴充點就是通過bean的後置處理器(BeanPostProcesser接口)來完成的,在初始化方法之前的擴充點對應的是BeanPostProcesser的postProcessBeforeInitialization()方法,為了便于描述可以簡單稱為bean後置處理器的前方法;初始化方法之後的擴充點對應的是BeanPostProcesser的postProcessAfterInitialization()方法,稱為bean後置處理器的後方法。之是以叫做bean的後置處理器,是因為它是在bean執行個體化完成之後執行的。

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;
	}
}
           

可以看到BeanPostProcesser是一個接口,開發者可以自己實作BeanPostProcesser接口來對bean進行某種功能的增強。但是BeanPostProcesser接口和Aware接口不一樣,Aware接口是bean直接繼承的,影響的隻有實作接口的這一個bean,而BeanPostProcesser接口是需要單獨實作的,影響的是所有的使用者bean,Spring啟動的時候會先注冊所有的bean後置處理器,然後建立bean,是以一旦一個BeanPostProcesser注冊成功就會在所有bean的建立過程中執行。

比如按如下代碼自定義一個Bean後置處理器:

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + ": Exec before init");
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + ": Exec after init");
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}
           

那麼所有的bean在建立過程中都會列印日志。

6.1 Spring中的Bean後置處理器

BeanPostProcesser是一個接口,我們可以自己實作這個接口,用于在bean初始化方法之前和之後進行功能增強。另一方面,Spring本身也運用這個接口來實作一些功能。典型的例子就是CommonAnnotationBeanPostProcessor和AnnotationAwareAspectJAutoProxyCreator,這兩個類都實作了BeanPostProcessor接口,是以都是Bean後置處理器,但是要注意這兩個類還實作了其它接口,功能是複雜的,并不是單純的Bean後置處理器。

6.1.1 典型例子之一:CommonAnnotationBeanPostProcessor

CommonAnnotationBeanPostProcessor這個類用來處理javax包中擴充的注解,這些注解包括@Resource、@WebServiceRef、@EJB、@PostConstruct、@PreDestroy,這五個注解中最為常用的是@Resource、@PostConstruct、@PreDestroy,但是這5個注解中隻有@PostConstruct和@PreDestroy這兩個注解用到了Bean後置處理器接口,看源碼:

// CommonAnnotationBeanPostProcessor繼承了InitDestroyAnnotationBeanPostProcessor,InitDestroyAnnotationBeanPostProcessor中有前方法和後方法的具體實作:
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    // 在bean的class中尋找是否有@PostConstruct和@PreDestroy這兩個注解,構造LifecycleMetadata對象
	LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
	try {
	    // 如果有@PostConstruct注解,将直接執行這個注解标注的方法
		metadata.invokeInitMethods(bean, beanName);
	}
	catch (InvocationTargetException ex) {
		...
	} catch (Throwable ex) {
		...
	}
	return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
	return bean;
}
           

可以看到CommonAnnotationBeanPostProcessor的前方法中掃描了bean的Class對象,看其中是否有@PostConstruct和@PreDestroy這兩個注解,然後構造出LifecycleMetadata對象。如果有@PostConstruct注解,這個注解标注的方法還會在前方法中立刻執行,@PreDestroy則是在bean銷毀之前執行。

其它的三個注解則沒有通過Bean後置處理器的前後方法進行處理,之前說過CommonAnnotationBeanPostProcessor是複雜的,不僅實作了BeanPostProcessor接口,也實作了其它接口,這幾個注解是通過其它接口定義的方法實作的,有興趣可以去看源碼,這裡聚焦于Bean後置處理器的功能,是以不進行展開。

我們知道@Resource注解和@Autowired注解對應,都用于進行依賴注入,@Resource通過CommonAnnotationBeanPostProcessor來實作,@Autowired則是通過AutowiredAnnotationBeanPostProcessor來實作的,這個類用來處理@AutoWired、@Values、 java.inject.Inject這三個注解,AutowiredAnnotationBeanPostProcessor同樣繼承了BeanPostProcessor這個接口,是以也是bean後置處理器,但是看源碼就會發現這個Bean後置處理器的前、後方法都是空方法,是以這些注解的功能都是通過其它接口定義的方法來實作的,之是以要把這個類定義為Bean的後置處理器隻是為了統一處理:Spring在啟動的時候會先掃描所有的Bean後置處理器并注冊,這個過程中會把AutowiredAnnotationBeanPostProcessor給注冊進去。

@Resource注解和@Autowired注解不使用bean後置處理器來實作的原因也很容易了解,這兩個注解是用來進行依賴注入的,spring建立bean的時候是先處理依賴注入(populateBean方法),然後才會執行bean後置處理器的前後方法(initializeBean方法),如果這兩個注解使用bean後置處理器來實作和建立bean的流程沖突了。

6.1.2 典型例子之二:AnnotationAwareAspectJAutoProxyCreator

AnnotationAwareAspectJAutoProxyCreator用于實作Spring的AOP功能。我們知道Spring的AOP底層用的是動态代理技術,這意味着在Spring容器中最終儲存的應該是增強過後的代理對象,而不是原始的對象,那Spring是怎麼做到這一點的呢?其實就是用到了BeanPostProcesser接口,在BeanPostProcesser接口的後方法中判斷目前bean是否需要植入切面邏輯,如果需要就生成一個代理對象。這個實作了BeanPostProcesser接口的類是AnnotationAwareAspectJAutoProxyCreator,看源碼:

// AnnotationAwareAspectJAutoProxyCreator繼承自AbstractAutoProxyCreator,在這個類中有後方法的實作
@Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (this.earlyProxyReferences.remove(cacheKey) != bean) { // 有關循環依賴
			    // 如果檢測到aop的設定,則建立代理對象
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}
           

這裡還牽扯到循環依賴相關的事項,這裡不進行展開,有單獨的篇章講解,見:源碼解析Spring循環依賴

6.2 BeanPostProcess的順序

從上面的講解可以看到Spring架構本身已經有了很多bean後置處理器,開發者自己也可以定義很多bean後置處理器,那這些bean後置處理器的執行順序是什麼樣的呢?我們來看注冊Bean後置處理器的實作邏輯,其實原本的注釋已經解釋的很詳細了:

// 注冊方法在PostProcessorRegistrationDelegate這個工具類中
public static void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {

	String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);

	// Register BeanPostProcessorChecker that logs an info message when
	// a bean is created during BeanPostProcessor instantiation, i.e. when
	// a bean is not eligible for getting processed by all BeanPostProcessors.
	int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
	beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

    // 處理bean後置處理器的主要思路是将bean後置處理器進行分離,将實作了PriorityOrdered接口、Ordered接口的bean後置處理器和剩餘的進行分離,分别排序之後再分别向spring容器注冊
	// Separate between BeanPostProcessors that implement PriorityOrdered,
	// Ordered, and the rest.
	List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
	List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
	List<String> orderedPostProcessorNames = new ArrayList<>();
	List<String> nonOrderedPostProcessorNames = new ArrayList<>();
	for (String ppName : postProcessorNames) {
		if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
			priorityOrderedPostProcessors.add(pp);
			if (pp instanceof MergedBeanDefinitionPostProcessor) {
				internalPostProcessors.add(pp);
			}
		}
		else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
			orderedPostProcessorNames.add(ppName);
		}
		else {
			nonOrderedPostProcessorNames.add(ppName);
		}
	}

    // 首先注冊實作了PriorityOrdered接口的bean後置處理器,這包括AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor
	// First, register the BeanPostProcessors that implement PriorityOrdered.
	sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
	registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

    // 然後注冊實作了Ordered接口的bean後置處理器,這包括AnnotationAwareAspectJAutoProxyCreator
	// Next, register the BeanPostProcessors that implement Ordered.
	List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
	for (String ppName : orderedPostProcessorNames) {
		BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
		orderedPostProcessors.add(pp);
		if (pp instanceof MergedBeanDefinitionPostProcessor) {
			internalPostProcessors.add(pp);
		}
	}
	sortPostProcessors(orderedPostProcessors, beanFactory);
	registerBeanPostProcessors(beanFactory, orderedPostProcessors);

    // 然後注冊剩餘的bean後置處理器,包括使用者自定義的普通bean後置處理器(沒有實作PriorityOrdered接口和Ordered接口),這些後置處理器按加載的順序放入到list中,加載是按照字典序加載的,是以這裡的順序也是字典序
	// Now, register all regular BeanPostProcessors.
	List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
	for (String ppName : nonOrderedPostProcessorNames) {
		BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
		nonOrderedPostProcessors.add(pp);
		if (pp instanceof MergedBeanDefinitionPostProcessor) {
			internalPostProcessors.add(pp);
		}
	}
	registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

    // 最後重新注冊internalPostProcessors中的bean後置處理器,也就是那些實作了MergedBeanDefinitionPostProcessor接口的bean後置處理器,包括AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor,重新注冊會将這些bean後置處理器從清單的前面移除,然後放到清單的最後。
	// Finally, re-register all internal BeanPostProcessors.
	sortPostProcessors(internalPostProcessors, beanFactory);
	registerBeanPostProcessors(beanFactory, internalPostProcessors);

	// Re-register post-processor for detecting inner beans as ApplicationListeners,
	// moving it to the end of the processor chain (for picking up proxies etc).
	beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}
           

上面代碼關鍵步驟我添加了中文注釋,處理bean後置處理器的主要思路是将bean後置處理器進行分離,将實作了PriorityOrdered接口、Ordered接口的Bean後置處理器和剩餘的進行分離,分别排序之後再分别向spring容器注冊,所謂注冊就是将這些Bean後置處理器放入到容器中的一個List中去,這裡要注意都是是先排序再注冊,放入到容器中的list之後不會再進行排序,也就是說先注冊的必然在list的前面。

AnnotationAwareAspectJAutoProxyCreator的order是Ordered.LOWEST_PRECEDENCE(值為2147483647),是最低的優先級;CommonAnnotationBeanPostProcessor也是這個值,也是最低的優先級;AutowiredAnnotationBeanPostProcessor是Ordered.LOWEST_PRECEDENCE - 2,比CommonAnnotationBeanPostProcessor略高一點;使用者自定義的bean後置處理器一般不會實作PriorityOrdered接口和Ordered接口,則是按照加載的順序排序,也就是字典序。按照源碼執行的流程,容器中bean後置處理器最終的順序是:

  • 1、AnnotationAwareAspectJAutoProxyCreator(用于AOP生成代理對象)
  • 2、使用者自定義的bean後置處理器
  • 3、AutowiredAnnotationBeanPostProcessor
  • 4、CommonAnnotationBeanPostProcessor(處理@PostConstruct等注解)