寫在前面
在《【String注解驅動開發】面試官再問你BeanPostProcessor的執行流程,就把這篇文章甩給他!》一文中,我們詳細的介紹了BeanPostProcessor的執行流程。那么,BeanPostProcessor在Spring底層是如何使用的?今天,我們就一起來探討下Spring的源碼,一探BeanPostProcessor在Spring底層的使用情況。
項目工程源碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation
BeanPostProcessor接口
我們先來看下BeanPostProcessor接口的源碼,如下所示。
package org.springframework.beans.factory.config;
import org.springframework.beans.BeansException;
import org.springframework.lang.Nullable;
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
可以看到,在BeanPostProcessor接口中,提供了兩個方法:postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法。postProcessBeforeInitialization()方法會在bean初始化之前調用,postProcessAfterInitialization()方法會在bean初始化之后調用。接下來,我們就分析下BeanPostProcessor接口在Spring中的實現。
注意:這里,我列舉幾個BeanPostProcessor接口在Spring中的實現類,來讓大家更加清晰的理解BeanPostProcessor接口在Spring底層的應用。
ApplicationContextAwareProcessor類
org.springframework.context.support.ApplicationContextAwareProcessor是BeanPostProcessor接口的實現類,這個類的作用是可以向組件中注入IOC容器,大致的源碼如下所示。
package org.springframework.context.support;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.EmbeddedValueResolver;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.lang.Nullable;
import org.springframework.util.StringValueResolver;
class ApplicationContextAwareProcessor implements BeanPostProcessor {
/****************************省略N多行代碼************************/
}
這里,省略了源碼的細節,只給出了類結構,感興趣的小伙伴們可自行翻閱Spring源碼進行查看,我這里的Spring版本為5.2.6.RELEASE。
那具體如何使用ApplicationContextAwareProcessor類向組件中注入IOC容器呢?別急,我用一個例子來說明下,相信小伙伴們看完后會有一種豁然開朗的感覺——哦,原來是它啊,我之前在項目中使用過的!
要想使用ApplicationContextAwareProcessor類向組件中注入IOC容器,我們就不得不提Spring中的另一個接口:ApplicationContextAware,如果需要向組件中注入IOC容器,可以使組件實現ApplicationContextAware接口。
例如,我們創建一個Employee類,使其實現ApplicationContextAware接口,此時,我們需要實現ApplicationContextAware接口的setApplicationContext()方法,在setApplicationContext()方法中有一個ApplicationContext類型的參數,這個就是IOC容器對象,我們可以在Employee類中定義一個ApplicationContext類型的成員變量,然后在setApplicationContext()方法中為這個成員變量賦值,此時就可以在Employee中的其他方法中使用ApplicationContext對象了,如下所示。
package io.mykit.spring.plugins.register.bean;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* @author binghe
* @version 1.0.0
* @description 測試ApplicationContextAware
*/
@Component
public class Employee implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
看到這里,相信不少小伙伴們都有一種很熟悉的感覺:沒錯,我之前也在項目中使用過!是的,這就是BeanPostProcessor在Spring底層的一種使用場景。至於上面的案例代碼為何會在setApplicationContext()方法中獲取到ApplicationContext對象,這就是ApplicationContextAwareProcessor類的功勞了!
接下來,我們就深入分析下ApplicationContextAwareProcessor類。
我們先來看下ApplicationContextAwareProcessor類中對於postProcessBeforeInitialization()方法的實現,如下所示。
@Override
@Nullable
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)){
return bean;
}
AccessControlContext acc = null;
if (System.getSecurityManager() != null) {
acc = this.applicationContext.getBeanFactory().getAccessControlContext();
}
if (acc != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareInterfaces(bean);
return null;
}, acc);
}
else {
invokeAwareInterfaces(bean);
}
return bean;
}
在bean初始化之前,首先對當前bean的類型進行判斷,如果當前bean的類型不是EnvironmentAware,不是EmbeddedValueResolverAware,不是ResourceLoaderAware,不是ApplicationEventPublisherAware,不是MessageSourceAware,也不是ApplicationContextAware,則直接返回bean。如果是上面類型中的一種類型,則最終會調用invokeAwareInterfaces()方法,並將bean傳遞給invokeAwareInterfaces()方法。invokeAwareInterfaces()方法又是個什么鬼呢?我們繼續看invokeAwareInterfaces()方法的源碼,如下所示。
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
可以看到invokeAwareInterfaces()方法的源碼比較簡單,就是判斷當前bean屬於哪種接口類型,則將bean強轉為哪種接口類型的對象,然后調用接口的方法,將相應的參數傳遞到接口的方法中。這里,我們在創建Employee類時,實現的是ApplicationContextAware接口,所以,在invokeAwareInterfaces()方法中,會執行如下的邏輯代碼。
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
我們可以看到,此時會將this.applicationContext傳遞到ApplicationContextAware接口的setApplicationContext()方法中。所以,我們在Employee類中的setApplicationContext()方法中就可以直接接收到ApplicationContext對象了。
我們也可以在IDEA中通過Debug的形式來看一下程序的執行過程,此時我們在Employee類的setApplicationContext()方法上設置斷點,如下所示。
接下來,我們以Debug的方式來運行SpringBeanTest類的testAnnotationConfig2()方法,運行后的效果如下圖所示。
在IDEA的左下角可以看到方法的調用堆棧,通過對方法調用棧的分析,我們看到在執行Employee類中的setApplicationContext()方法之前,執行了ApplicationContextAwareProcessor類的invokeAwareInterfaces方法,如下所示。
當我們點擊方法調用棧中的invokeAwareInterfaces()方法時,代碼的執行定位到如下一行代碼。
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
和我們之前分析的邏輯一致。
BeanValidationPostProcessor類
org.springframework.validation.beanvalidation.BeanValidationPostProcessor類主要是用來為bean進行校驗操作,當我們創建bean,並為bean賦值后,我們可以通過BeanValidationPostProcessor類為bean進行校驗操作。BeanValidationPostProcessor類的結構如下所示。
package org.springframework.validation.beanvalidation;
import java.util.Iterator;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
public class BeanValidationPostProcessor implements BeanPostProcessor, InitializingBean {
/*******************************省略N行代碼**********************************/
}
這里,我們也來看看postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法的實現,如下所示。
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (!this.afterInitialization) {
doValidate(bean);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (this.afterInitialization) {
doValidate(bean);
}
return bean;
}
可以看到,在postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法中的主要邏輯都是調用doValidate()方法對bean進行校驗,只不過在兩個方法中都會對afterInitialization這個boolean類型的成員變量進行判斷,如果afterInitialization的值為false,則在postProcessBeforeInitialization()方法中調用doValidate()方法對bean進行校驗;如果afterInitialization的值為true,則在postProcessAfterInitialization()方法中調用doValidate()方法對bean進行校驗。
InitDestroyAnnotationBeanPostProcessor類
org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor類主要用來處理@PostConstruct注解和@PreDestroy注解。
例如,我們之前創建的Cat類中就使用了@PostConstruct注解和@PreDestroy注解,如下所示。
package io.mykit.spring.plugins.register.bean;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
/**
* @author binghe
* @version 1.0.0
* @description 測試@PostConstruct注解和@PreDestroy注解
*/
public class Cat {
public Cat(){
System.out.println("Cat類的構造方法...");
}
public void init(){
System.out.println("Cat的init()方法...");
}
@PostConstruct
public void postConstruct(){
System.out.println("Cat的postConstruct()方法...");
}
@PreDestroy
public void preDestroy(){
System.out.println("Cat的preDestroy()方法...");
}
public void destroy(){
System.out.println("Cat的destroy()方法...");
}
}
那么,在Cat類中使用了 @PostConstruct注解和@PreDestroy注解來標注方法,Spring怎么就知道什么時候執行 @PostConstruct注解標注的方法,什么時候執行@PreDestroy標注的方法呢?這就要歸功於InitDestroyAnnotationBeanPostProcessor類的實現了。
接下來,我們也通過Debug的方式來跟進下代碼的執行流程。首先,在Cat類的postConstruct()方法上打上斷點,如下所示。
接下來,我們以Debug的方式運行BeanLifeCircleTest類的testBeanLifeCircle04()方法,效果如下所示。
我們還是帶着問題來分析,Spring怎么就能定位到使用@PostConstruct注解標注的方法呢?通過分析方法的調用棧我們發現了在進入使用@PostConstruct注解標注的方法之前,Spring調用了InitDestroyAnnotationBeanPostProcessor類的postProcessBeforeInitialization()方法,如下所示。
在InitDestroyAnnotationBeanPostProcessor類的postProcessBeforeInitialization()方法中,首先會找到bean中有關生命周期的注解,比如@PostConstruct注解等,找到這些注解之后,則將這些信息賦值給LifecycleMetadata類型的變量metadata,之后調用metadata的invokeInitMethods()方法,通過反射來調用標注了@PostConstruct注解的方法。這就是為什么標注了@PostConstruct注解的方法被Spring執行。
AutowiredAnnotationBeanPostProcessor類
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor類主要是用於處理標注了@Autowired注解的變量或方法。
Spring為何能夠自動處理標注了@Autowired注解的變量或方法,就交給小伙伴們自行分析了。大家可以寫一個測試方法並通過方法調用堆棧來分析AutowiredAnnotationBeanPostProcessor類的源碼,從而找到自己想要的答案。
好了,咱們今天就聊到這兒吧!別忘了給個在看和轉發,讓更多的人看到,一起學習一起進步!!
項目工程源碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation
寫在最后
如果覺得文章對你有點幫助,請微信搜索並關注「 冰河技術 」微信公眾號,跟冰河學習Spring注解驅動開發。公眾號回復“spring注解”關鍵字,領取Spring注解驅動開發核心知識圖,讓Spring注解驅動開發不再迷茫。