Spring 執行順序:PostProcessor 接口

回目錄

代碼:https://gitee.com/free/boot-order/tree/master/src/main/java/com/github/abel533/postprocessor

PostProcessor 類接口如下所示:

在這裏插入圖片描述

其中 BeanPostProcessor 是最常見的一個系列,BeanFactoryPostProcessor 和 EnvironmentPostProcessor 不常用。

由於上圖所示的繼承關係和不同 PostProcessor 使用的要求限制,這裏不能同時使用所有這些 PostProcessor,所以下面是針對不同情況去測試方法的執行順序。

因爲 BeanFactoryPostProcessor 沒有使用限制,所以後續所有例子中都會實現該接口,而且該接口都是第一個調用的。

BeanPostProcessor 系列

用途:允許修改新的 bean 實例的工廠鉤子函數,例如檢查標記接口或者用代理包裝 bean。
通常在 postProcessBeforeInitialization 方法中處理標記接口,在 postProcessAfterInitialization 中處理代理 beans。
該方法會對上下文中的所有 bean 進行處理,因此根據 bean 的數量,會執行多次,所以後續日誌中都會頻繁出現調用的日誌。

在 4 個子接口中,都針對 BeanPostProcessor 增加了額外的處理方法,額外的方法都是在

BeanPostProcessor 接口

src\main\java\com\github\abel533\postprocessor\base

由於執行多次的原因,下面貼出部分執行順序:

  1. BeanFactoryPostProcessor#postProcessBeanFactory
  2. BeanPostProcessor#postProcessBeforeInitialization - org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat
  3. BeanPostProcessor#postProcessBeforeInitialization - org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration
  4. BeanPostProcessor#postProcessAfterInitialization - org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration
  5. BeanPostProcessor#postProcessBeforeInitialization - transactionAttributeSource
  6. BeanPostProcessor#postProcessAfterInitialization - transactionAttributeSource
  7. BeanPostProcessor#postProcessBeforeInitialization - transactionInterceptor
  8. BeanPostProcessor#postProcessAfterInitialization - transactionInterceptor
  9. BeanPostProcessor#postProcessBeforeInitialization - org.springframework.transaction.config.internalTransactionAdvisor
  10. BeanPostProcessor#postProcessAfterInitialization - org.springframework.transaction.config.internalTransactionAdvisor
  11. BeanPostProcessor#postProcessAfterInitialization - org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat

AbstractApplicationContext#refresh 中可以看到 PostProcessor 的執行順序,代碼如下(有刪減):

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            // 執行 0
            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // 註冊所有 BeanPostProcessors,這裏會區分是否繼承了 PriorityOrdered 和 Ordered
            // 執行的優先級按照 PriorityOrdered > Ordered > 無
            registerBeanPostProcessors(beanFactory);

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // 執行 1~end
            // Initialize other special beans in specific context subclasses.
            onRefresh();
        }
    }
}

1~10 具體在 AbstractAutowireCapableBeanFactory#initializeBean 初始化 Bean 的時候,先調用了 postProcessBeforeInitialization 方法,然後是 Bean 的初始化方法,隨後就是 postProcessAfterInitialization 方法,代碼如下(有刪減):。

	protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
		if (mbd == null || !mbd.isSynthetic()) {
		    // postProcessBeforeInitialization 方法
			wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
		}
		try {
			invokeInitMethods(beanName, wrappedBean, mbd);
		}
		catch (Throwable ex) {
		}
		if (mbd == null || !mbd.isSynthetic()) {
		    // postProcessAfterInitialization 方法
			wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
		}

		return wrappedBean;
	}

上述執行順序中,執行 1 的時候處理的 org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat,在之後的 postProcessAfterInitialization 方法中,由於獲取 advisor 進行代理,又進入了其他 bean 的 PostProcessor 方法,直到 10 的時候,才返回到 1 對應的 bean 上。

SmartInstantiationAwareBeanPostProcessor

SmartInstantiationAwareBeanPostProcessor 繼承了 InstantiationAwareBeanPostProcessor,這倆接口都是用於 Spring 框架內部使用的接口,接口方法可以根據需要隨時添加(不對外兼容)不推薦使用,
如果要使用,可以使用 InstantiationAwareBeanPostProcessorAdapter 類,該類會保證所有默認方法實現的正確性。

下面以 InstantiationAwareBeanPostProcessorAdapter 爲例查看執行順序。

爲了能完整的測試所有的接口方法,特別實現了下面兩個循環依賴的類:

public class User {
    @Autowired
    private UserExt ext;
    private String id;

    public User() {
    }
    //其他
}

public class UserExt {
    @Autowired
    private User user;
    private String name;

    public UserExt() {
    }
    //其他
}

由於所有 Bean 都會觸發接口輸出日誌,因此在日誌中過濾只顯示了其中 3 個 bean 的日誌,執行順序如下:

  1. InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation - org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat
  2. SmartInstantiationAwareBeanPostProcessor#determineCandidateConstructors - org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat
  3. InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation - org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat
  4. InstantiationAwareBeanPostProcessor#postProcessProperties - org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat
  5. BeanPostProcessor#postProcessBeforeInitialization - org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat
  6. BeanPostProcessor#postProcessAfterInitialization - org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat
  7. InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation - user
  8. InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation - user
  9. InstantiationAwareBeanPostProcessor#postProcessProperties - user
  10. InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation - userExt
  11. InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation - userExt
  12. InstantiationAwareBeanPostProcessor#postProcessProperties - userExt
  13. SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference - user
  14. BeanPostProcessor#postProcessBeforeInitialization - userExt
  15. BeanPostProcessor#postProcessAfterInitialization - userExt
  16. BeanPostProcessor#postProcessBeforeInitialization - user
  17. BeanPostProcessor#postProcessAfterInitialization - user

SmartInstantiationAwareBeanPostProcessor#predictBeanType 執行很頻繁,從順序中去掉

不存在依賴的前提下,就是按照 ServletWebServerFactoryConfiguration$EmbeddedTomcat 的順序執行的,摘抄如下:

  1. InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
    在實例化目標bean之前應用此BeanPostProcessor。
  2. SmartInstantiationAwareBeanPostProcessor#determineCandidateConstructors
    確定要用於給定bean的候選構造函數。
  3. InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation
    在bean實例化之後,通過構造函數或工廠方法,但在Spring屬性填充(來自顯式屬性或自動裝配)之前執行操作。
  4. InstantiationAwareBeanPostProcessor#postProcessProperties
    在工廠將它們應用於給定bean之前對給定屬性值進行後處理,不需要屬性描述符。
  5. BeanPostProcessor#postProcessBeforeInitialization
    在任何bean初始化回調(如InitializingBean afterPropertiesSet 或自定義init方法)之前,將此BeanPostProcessor應用於給定的新bean實例。
  6. BeanPostProcessor#postProcessAfterInitialization
    在任何bean初始化回調(如InitializingBean afterPropertiesSet 或自定義init方法)之後,將此BeanPostProcessor應用於給定的新bean實例。

接下來看看 User 和 UserExt 兩個循環依賴 bean 的創建過程。

  1. InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation - user
    在實例化目標bean之前應用此BeanPostProcessor。
  2. InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation - user
    在bean實例化之後,通過構造函數或工廠方法,但在Spring屬性填充(來自顯式屬性或自動裝配)之前執行操作。
  3. InstantiationAwareBeanPostProcessor#postProcessProperties - user
    在工廠將它們應用於給定bean之前對給定屬性值進行後處理,不需要屬性描述符。
  4. InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation - userExt
    在實例化目標bean之前應用此BeanPostProcessor。
  5. InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation - userExt
    在bean實例化之後,通過構造函數或工廠方法,但在Spring屬性填充(來自顯式屬性或自動裝配)之前執行操作。
  6. InstantiationAwareBeanPostProcessor#postProcessProperties - userExt
    在工廠將它們應用於給定bean之前對給定屬性值進行後處理,不需要屬性描述符。
  7. SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference - user
    獲取早期訪問指定bean的引用,通常用於解析循環引用。
  8. BeanPostProcessor#postProcessBeforeInitialization - userExt
    在任何bean初始化回調(如InitializingBean afterPropertiesSet 或自定義init方法)之前,將此BeanPostProcessor應用於給定的新bean實例。
  9. BeanPostProcessor#postProcessAfterInitialization - userExt
    在任何bean初始化回調(如InitializingBean afterPropertiesSet 或自定義init方法)之後,將此BeanPostProcessor應用於給定的新bean實例。
  10. BeanPostProcessor#postProcessBeforeInitialization - user
    在任何bean初始化回調(如InitializingBean afterPropertiesSet 或自定義init方法)之前,將此BeanPostProcessor應用於給定的新bean實例。
  11. BeanPostProcessor#postProcessAfterInitialization - user
    在任何bean初始化回調(如InitializingBean afterPropertiesSet 或自定義init方法)之後,將此BeanPostProcessor應用於給定的新bean實例。

從這兒的邏輯看到,初始化 User 的時候,由於需要注入 UserExt,就去初始化 UserExt 了,由於 UserExt 中也需要 User,並且 User 已經有了早期的 bean 引用,因此 UserExt 初始化完成,此後返回 User 的初始化過程,至此兩個 bean 創建成功。

這裏的示例中,都是通過屬性注入的,因此可以成功。如果 User 使用構造參數注入 UserExt,由於這裏掃描順序 User 更早,就無法解決這個循環依賴,會報錯如下:

***************************
APPLICATION FAILED TO START
***************************

Description:

The dependencies of some of the beans in the application context form a cycle:

┌─────┐
|  user defined in class path resource [com/github/abel533/postprocessor/instantiation/CircleConfig.class]
↑     ↓
|  userExt (field private com.github.abel533.postprocessor.instantiation.User com.github.abel533.postprocessor.instantiation.UserExt.user)
└─────┘

但是如果調整這兩個 Bean 的順序(在 CircleConfig 中),就會成功。由於初始化順序的不確定性,構造參數方式很難解決循環依賴,所以存在循環依賴的情況時,使用屬性或方法方式注入。

EnvironmentPostProcessor

這是 Spring Boot 特有的接口,會在刷新應用程序上下文之前調用。支持通過 Ordered 接口進行排序。

必須在 META-INF/spring.factories 使用此類的完全限定名稱作爲鍵來註冊 EnvironmentPostProcessor 實現 。

例如本例:

org.springframework.boot.env.EnvironmentPostProcessor=com.github.abel533.postprocessor.environment.EnvironmentPostProcessorImpl

由於這個配置會影響其他測試的執行順序(EnvironmentPostProcessor 肯定是第一個),所以本項目中註釋了上述配置。

該接口比所有其他 PostProcessor 執行的都早,由於接口特殊,這裏不再繼續往下進行分析。

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