BeanPostProcessor 源碼解析

1. 解釋

BeanPostProcessor 就是Bean 的後置處理器 ,主要作用就是 Bean 實例之後,在 initialization 之前和之後 調用自定義的方法 改變一些屬性
這裏 的 initialization 包含: Bean 裏面定義的 initMethod , InitializingBean 的 afterPropertiesSet

此外 還有一個 annotation @PostConstruct 和 @PreDestroy,也是可以對Bean 進行擴展的,但是他們的邏輯 和上面的 InitializingBean 和 自定義 的initMethod 的 底層一點點區別,他們 類似 BeanPostProcessor ,他對應的 類是 CommonAnnotationBeanPostProcessor

看一下 BeanPostProcessor 的接口
我用的 是 spring-beans: 5.1.7.RELEASE 版本,下面兩個方法都加了 default

/**
主要作用就是 :允許對 Bean instances 自定義修改
ApplicationContexts 會自動 檢查到 BeanPostProcessor beans 並將它們應用於隨後創建的任何bean
*/
public interface BeanPostProcessor {
    /**
    * 在 進行調用 initialization 方法之前 先運行此方法,這裏的 initialization 比如 
    *  (InitializingBean's {@code afterPropertiesSet},或者 or 自定義的 init-method
	* 默認是返回Bean 本身 
    */
	@Nullable
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

    /**
    * 在 進行調用 initialization 方法之後 運行此方法, 這裏的 initialization 比如 
    *  (InitializingBean's {@code afterPropertiesSet},或者 or 自定義的 init-method
    * 如果是 FactoryBean, 那麼 此方法不僅作用於FactoryBean 實例,還作用於 此FactoryBean 創建的對象(自從 spring 2.0 ) ,此post-processor  通過 相應的check 可以決定是作用於 FactoryBean 還是 創建的對象,或者兩者
	* 默認是返回Bean 本身 
    */
	@Nullable
	default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
}

2. demo

2.1 common Demo

  1. 創建一個Bean
@Configuration
public class BeanA {
    private static final Logger log = LoggerFactory.getLogger(BeanA.class);

    public BeanA() {
        System.out.println("=======Bean A  構造函數======");
    }

}
  1. 創建一個自定義的BeanPostProcessor
@Component
public class MysqlBeanPostProcessor implements BeanPostProcessor {

    public MysqlBeanPostProcessor() {
        System.out.println("=======run  MysqlBeanPostProcessor 構造函數====");
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof BeanA) {
            // 這裏我們可以對bean 做一下擴展
            System.out.println("=====before Initialization  運行");
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof BeanA) {
            System.out.println("=====after  Initialization  運行");
        }
        return bean;
    }
}
  1. 弄個啓動類 運行一下
@SpringBootApplication
public class ApplicationMain {
    public static void main(String[] args) {
        SpringApplication.run(ApplicationMain.class, args);
    }
}
  1. 運行結果:
    1.可以看到 是先注入我們自定義的MysqlBeanPostProcessor ,然後作用於每一個Bean
  2. 先在Bean 實例之後,然後先運行before ,再運行 after 方法.(這個不夠明朗,弄一下下面一個例子)
=======run  MysqlBeanPostProcessor 構造函數====
INFO 3360 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
INFO 3360 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
INFO 3360 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.19]
INFO 3360 --- [           main] o.a.catalina.core.AprLifecycleListener   : Loaded APR based Apache Tomcat Native library [1.2.23] using APR version [1.7.0].
INFO 3360 --- [           main] o.a.catalina.core.AprLifecycleListener   : APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true].
INFO 3360 --- [           main] o.a.catalina.core.AprLifecycleListener   : APR/OpenSSL configuration: useAprConnector [false], useOpenSSL [true]
INFO 3360 --- [           main] o.a.catalina.core.AprLifecycleListener   : OpenSSL successfully initialized [OpenSSL 1.1.1c  28 May 2019]
INFO 3360 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
INFO 3360 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1135 ms
=======Bean A  構造函數======
=====before Initialization  運行
=====after  Initialization  運行

2.2 demo 增加 InitializingBean

將上面的BeanA 方法 實現一下 InitializingBean

@Configuration
public class BeanA implements InitializingBean {
    private static final Logger log = LoggerFactory.getLogger(BeanA.class);

    public BeanA() {
        System.out.println("=======Bean A  構造函數======");
    }
    
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("=======Bean A  運行 InitializingBean 的 afterPropertiesSet======");
    }

}

運行結果:

=======run  MysqlBeanPostProcessor 構造函數====
INFO 4200 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
INFO 4200 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
INFO 4200 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.19]
INFO 4200 --- [           main] o.a.catalina.core.AprLifecycleListener   : Loaded APR based Apache Tomcat Native library [1.2.23] using APR version [1.7.0].
INFO 4200 --- [           main] o.a.catalina.core.AprLifecycleListener   : APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true].
INFO 4200 --- [           main] o.a.catalina.core.AprLifecycleListener   : APR/OpenSSL configuration: useAprConnector [false], useOpenSSL [true]
INFO 4200 --- [           main] o.a.catalina.core.AprLifecycleListener   : OpenSSL successfully initialized [OpenSSL 1.1.1c  28 May 2019]
INFO 4200 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
INFO 4200 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1093 ms
=======Bean A  構造函數======
=====before Initialization  運行
=======Bean A  運行 InitializingBean 的 afterPropertiesSet======
=====after  Initialization  運行

2.3 demo 增加 @PostConstruct

修改 BeanA 代碼如下:

@Configuration
public class BeanA implements InitializingBean {
    private static final Logger log = LoggerFactory.getLogger(BeanA.class);

    public BeanA() {
        System.out.println("=======Bean A  構造函數======");
    }


    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("=======Bean A  運行 InitializingBean 的 afterPropertiesSet======");
    }

    @PostConstruct
    public void init(){
        System.out.println("=======Bean A  運行 PostConstruct 的 初始化======");
    }
}

運行結果如下:
說明 beforxxx 、 afterxxx、 方法是在 @PostConstruct 前後運行

=======run  MysqlBeanPostProcessor 構造函數====
INFO 22072 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
INFO 22072 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
INFO 22072 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.19]
INFO 22072 --- [           main] o.a.catalina.core.AprLifecycleListener   : Loaded APR based Apache Tomcat Native library [1.2.23] using APR version [1.7.0].
INFO 22072 --- [           main] o.a.catalina.core.AprLifecycleListener   : APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true].
INFO 22072 --- [           main] o.a.catalina.core.AprLifecycleListener   : APR/OpenSSL configuration: useAprConnector [false], useOpenSSL [true]
INFO 22072 --- [           main] o.a.catalina.core.AprLifecycleListener   : OpenSSL successfully initialized [OpenSSL 1.1.1c  28 May 2019]
INFO 22072 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
INFO 22072 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1325 ms
=======Bean A  構造函數======
=====before Initialization  運行
=======Bean A  運行 PostConstruct 的 初始化======
=======Bean A  運行 InitializingBean 的 afterPropertiesSet======
=====after  Initialization  運行

3. 源碼分析

3.1 運行時機

接下來我們 看一下如何運行的,以及分析一下源碼,在 打印日誌的地方 打個斷點,Debug 一下,如下圖:
從下面我們可以看到
在這裏插入圖片描述
我們從上面的左下面的框框裏面可以看到 這裏是在 運行refresh() 裏面方法時觸發的,refresh這塊後續整理一下//TODO,本次我們就從 AbstractAutowireCapableBeanFactory 類的 initializeBean 方法 開始解析
在這裏插入圖片描述
這裏的流程也很清晰,大致爲:
1.首先判斷 是否設置了SecurityManagers ,如果設置了,就進行相關的權限配置 和 Aware 的擴展
2. 如果沒有 直接進行 Aware 的擴展 ,Aware 這塊就是 對相關的Bean 額外的配置一些響應的屬性 ,代碼塊如下//TODO 後續整理一下
3. 判斷bean 是不是應用程序自己定義的,如果不是 ,那就 遍歷 運行 BeanPostProcessors 的postProcessBeforeInitialization 方法 這裏有一個 getBeanPostProcessors() 方法,裏面是獲取所有的 實現了BeanPostProcessors 接口的類,這裏是如果獲取到的呢,在何時放進去的呢,下面會提到.

上面說到 的 註解 @PostConstruct 是在這一步運行的 ,相當於 BeanPostProcessors 的Beforxxxx 方法
4. 運行 invokeInitMethods ,這裏有兩種 ,一種是 繼承了 InitializingBean ,那就實現 對應的afterPropertiesSet() 方法,或者是自定義的 InitMethod ,通過反射 去調用 上面的InitializingBean 和 自定義的initMethod 是在這一步運行
5. 判斷bean 是不是應用程序自己定義的,如果不是 ,那就 遍歷 運行 BeanPostProcessors 的postProcessAfterInitialization @PreDestory是在這一步 完成

// 這裏就是對Bean 進行屬性的配置
	private void invokeAwareMethods(final String beanName, final Object bean) {
		if (bean instanceof Aware) {
			if (bean instanceof BeanNameAware) {
				((BeanNameAware) bean).setBeanName(beanName);
			}
			if (bean instanceof BeanClassLoaderAware) {
				ClassLoader bcl = getBeanClassLoader();
				if (bcl != null) {
					((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
				}
			}
			if (bean instanceof BeanFactoryAware) {
				((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
			}
		}
	}

// 這裏就是 遍歷的去運行 BeanPostProcessors 的postProcessBeforeInitialization 方法
	@Override
	public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
			throws BeansException {

		Object result = existingBean;
		for (BeanPostProcessor processor : getBeanPostProcessors()) {
			Object current = processor.postProcessBeforeInitialization(result, beanName);
			if (current == null) {
				return result;
			}
			result = current;
		}
		return result;
	}

3.2 getBeanPostProcessors()

上面提到 getBeanPostProcessors()裏面的值是何時放進去的,其實 是在 refresh() 方法裏面的 registerBeanPostProcessors(beanFactory) 裏面進行收集的.
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章