萬字長文帶你窺探Spring中所有的擴展點

来源:https://www.cnblogs.com/seven97-top/p/18402945
-Advertisement-
Play Games

寫在前面 Spring的核心思想就是容器,當容器refresh的時候,外部看上去風平浪靜,其實內部則是一片驚濤駭浪,汪洋一片。Springboot更是封裝了Spring,遵循約定大於配置,加上自動裝配的機制。很多時候我們只要引用了一個依賴,幾乎是零配置就能完成一個功能的裝配。 由spring提供的、 ...


寫在前面

Spring的核心思想就是容器,當容器refresh的時候,外部看上去風平浪靜,其實內部則是一片驚濤駭浪,汪洋一片。Springboot更是封裝了Spring,遵循約定大於配置,加上自動裝配的機制。很多時候我們只要引用了一個依賴,幾乎是零配置就能完成一個功能的裝配。

由spring提供的、在容器或bean生命周期各個階段、供spring框架回調使用的函數方法,即為擴展點。擴展點體現了Spring框架的靈活性、業務親和性。使開發人員可以在不修改spring源碼的情況下,對容器和bean的行為、屬性進行額外的管理。

想要把自動裝配玩的轉,就必須要瞭解spring對於bean的構造生命周期以及各個擴展介面,當然瞭解了bean的各個生命周期也能促進我們加深對spring的理解。業務代碼也能合理利用這些擴展點寫出更優雅的代碼。

在網上搜索spring擴展點,發現很少有博文說的很全的,只有一些常用的擴展點的說明。所以在這篇文章里,我總結了幾乎Spring & Springboot所有的擴展介面,各個擴展點的使用場景,並整理出一個bean在spring中從被載入到初始化到銷毀的所有可擴展點的順序調用圖。

本文不講原理,只將擴展點與使用方式講清楚,特別是調用順序,原理可以移步IOC系列文章Bean的生命周期,後續會不斷更新對應原理及源碼解析。大家可以把這篇文章當成一個工具書,當忘了執行順序時,或者忘瞭如何使用這個擴展方式時,可以再回過頭來看看。

spring擴展點執行順序

ApplicationContextInitializer

org.springframework.context.ApplicationContextInitializer

介紹

這是整個spring容器在刷新之前初始化ConfigurableApplicationContext的回調介面,簡單來說,就是在容器刷新refresh之前調用 此類的initialize方法。這個介面的主要目的是在 Spring 應用上下文初始化的早期階段進行一些配置或調整,以便在上下文載入後可以使用這些配置。

此介面,Spring Framework自己沒有提供任何的實現類,但在SpringBoot對它有較多的擴展實現。

使用場景

  1. 在應用啟動時進行環境配置:可以使用 ApplicationContextInitializer 來在應用上下文初始化時進行一些環境相關的配置,例如設置系統屬性、載入外部配置文件等。
public class EnvironmentInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        ConfigurableEnvironment environment = applicationContext.getEnvironment();
        environment.setActiveProfiles("development");
        System.out.println("配置文件設置為development");
    }
}
  1. 註冊自定義的 BeanFactoryPostProcessor 或者 BeanPostProcessorApplicationContextInitializer 可以用來註冊自定義的 BeanFactoryPostProcessor 或者 BeanPostProcessor,以便在 Bean 初始化之前或之後進行某些自定義處理。
public class CustomBeanFactoryPostProcessorInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        applicationContext.addBeanFactoryPostProcessor(beanFactory -> {
            // 添加自定義的 BeanFactoryPostProcessor
            System.out.println("添加了自定義BeanFactory後處理器...");
        });
    }
}
  1. 動態地添加 PropertySource:可以在初始化過程中動態地添加 PropertySource,以便後續的 Bean 定義和初始化過程中可以使用這些屬性。
public class PropertySourceInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        MutablePropertySources propertySources = applicationContext.getEnvironment().getPropertySources();
        propertySources.addFirst(new MapPropertySource("customPropertySource", Collections.singletonMap("customKey", "customValue")));
        System.out.println("已添加自定義屬性源");
    }
}

Spring環境下添加擴展點

在Spring環境下自定義實現一個ApplicationContextInitializer讓並且它生效的方式有三種:

  1. 手動調用的setXXX方法添加
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();

// Add initializer
context.addApplicationListener(new TestApplicationContextInitializer());

// Set config locations and refresh context
context.setConfigLocation("classpath:applicationContext.xml");
context.refresh();

// Use the context
// ...

context.close();
  1. Spring 的 XML 配置文件中註冊
<context:initializer class="com.seven.springsrpingbootextentions.extentions.TestApplicationContextInitializer"/>
  1. web.xml 文件配置
<context-param>
    <param-name>contextInitializerClasses</param-name>
    <param-value>com.seven.springsrpingbootextentions.extentions.TestApplicationContextInitializer</param-value>
</context-param>

SpringBoot環境下添加擴展點

示例,展示瞭如何實現一個ApplicationContextInitializer來添加一個自定義的屬性源:

public class TestApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        MutablePropertySources propertySources = applicationContext.getEnvironment().getPropertySources();
        
        // 創建自定義的屬性源
        Map<String, Object> customProperties = new HashMap<>();
        customProperties.put("custom.property", "custom value");
        MapPropertySource customPropertySource = new MapPropertySource("customPropertySource", customProperties);
        
        // 將自定義屬性源添加到應用程式上下文的屬性源列表中
        propertySources.addFirst(customPropertySource);
    }
}

在SpringBoot中讓它生效的方式也有三種:

  1. 在啟動類中用springApplication.addInitializers(new TestApplicationContextInitializer())語句加入
@SpringBootApplication
public class MySpringExApplication {

    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(MySpringExApplication.class);
        application.addInitializers(new TestApplicationContextInitializer()); // 直接在SpringApplication中添加
        application.run(args);
    }
}
  1. application配置文件 配置 com.seven.springsrpingbootextentions.extentions.TestApplicationContextInitializer
# application.yml文件
context:
  initializer:
    classes: com.seven.springsrpingbootextentions.extentions.TestApplicationContextInitializer
  1. Spring SPI擴展,在spring.factories中加入(官方推薦):
com.seven.springsrpingbootextentions.extentions.TestApplicationContextInitializer

SpringBoot內置的ApplicationContextInitializer

  • DelegatingApplicationContextInitializer:使用環境屬性context.initializer.classes指定的初始化器(initializers)進行初始化工作,如果沒有指定則什麼都不做。通過它使得我們可以把自定義實現類配置在application.properties里成為了可能。

  • ContextIdApplicationContextInitializer:設置Spring應用上下文的ID,Id設置為啥值會參考環境屬性:

    • spring.application.name
    • vcap.application.name
    • spring.config.name
    • spring.application.index
    • vcap.application.instance_index
    • 如果這些屬性都沒有,ID使用application。
  • ConfigurationWarningsApplicationContextInitializer:對於一般配置錯誤在日誌中作出警告

  • ServerPortInfoApplicationContextInitializer:將內置servlet容器實際使用的監聽埠寫入到Environment環境屬性中。這樣屬性local.server.port就可以直接通過@Value註入到測試中,或者通過環境屬性Environment獲取。

  • SharedMetadataReaderFactoryContextInitializer:創建一個 SpringBoot 和 ConfigurationClassPostProcessor 共用的 CachingMetadataReaderFactory對象。實現類為:ConcurrentReferenceCachingMetadataReaderFactory

  • ConditionEvaluationReportLoggingListener:將ConditionEvaluationReport寫入日誌。

BeanFactoryPostProcessor

org.springframework.beans.factory.config.BeanFactoryPostProcessor

介紹

這個介面是beanFactory的擴展介面,調用時機在spring在讀取beanDefinition信息之後,實例化bean之前。雖然此時不能再註冊beanDefinition,但是可以趁著bean沒有實例化,可以修改 Spring 容器啟動時修改其內部的 BeanDefinition。通過實現 BeanFactoryPostProcessor 介面,開發者可以在 Bean 實例化之前修改 Bean 的定義元數據,例如Scope、依賴查找方式、初始化方法、修改屬性值、添加額外的元數據等,進而影響初始化行為。

在應用程式啟動時,Spring容器會自動檢測並調用所有實現了BeanFactoryPostProcessor介面的類的postProcessBeanFactory方法。開發人員可以利用這個方法來實現自定義的邏輯,從而實現一些高級的自定義邏輯和功能擴展。此方法只調用一次,同時記住不要在這裡做Bean的實例化

使用場景

  1. 修改 Bean 屬性:可以動態地改變某些配置屬性或者註入額外的依賴。
public class PropertyModifierBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("myBean");
        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
        propertyValues.addPropertyValue("propertyName", "newValue");
    }
}
  1. 動態註冊 Bean:可以根據配置文件或者系統環境變數來決定是否註冊某個 Bean。
public class ConditionalBeanRegistrar implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        if (someCondition()) {
            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(MyBean.class);
            beanFactory.registerBeanDefinition("myConditionalBean", beanDefinitionBuilder.getBeanDefinition());
        }
    }

    private boolean someCondition() {
        // 自定義條件邏輯
        return true;
    }
}
  1. 修改 Bean 定義:可以修改 Bean 的作用域、初始化和銷毀方法等定義信息。
public class ScopeModifierBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("myBean");
        beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
    }
}
  1. 屬性占位符替換:可以使用 PropertyPlaceholderConfigurer 實現 BeanFactoryPostProcessor 介面,來替換 Bean 定義中的屬性占位符。
public class CustomPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {

    @Override
    protected void processProperties(ConfigurableListableBeanFactory beanFactory, Properties props)
            throws BeansException {
        super.processProperties(beanFactory, props);
        // 自定義屬性處理邏輯
    }
}

其它使用場景:

  1. 配置中心集成:當需要從外部配置中心(如 Spring Cloud Config 或 Apache Zookeeper)動態載入配置並修改 Bean 定義時,可以使用 BeanFactoryPostProcessor
  2. 多環境支持:根據不同的環境(如開發、測試、生產環境)動態修改 Bean 的定義,確保不同環境下的 Bean 配置有所不同。
  3. 動態註冊 Bean:在應用運行時,根據條件動態註冊或者取消 Bean,比如在某些特定條件下才需要註冊某些 Bean。
  4. 複雜業務應用:有時候會需要根據複雜的業務規則動態調整 Bean 的配置,這時候 BeanFactoryPostProcessor 非常有用。

BeanDefinitionRegistryPostProcessor

org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor

介紹

BeanDefinitionRegistryPostProcessor為容器級後置處理器。容器級的後置處理器會在Spring容器初始化後、刷新前執行一次,用於動態註冊Bean到容器

通過 BeanFactoryPostProcessor 的子類 BeanDefinitionRegistryPostProcessor,可以註冊一個你自己的BeanDefinition對象到容器中,等待容器內部依次調用進行對象實例化就能當bean用了。

BeanDefinitionRegistryPostProcessor用於在bean解析後實例化之前通過BeanDefinitionRegistry對BeanDefintion進行增刪改查。

前文介紹的BeanFactoryPostProcessor是這個介面的父類,因此實現BeanDefinitionRegistryPostProcessor這個介面,也可以重寫其父類。但實現了BeanDefinitionRegistryPostProcessor的postProcessBeanFactory方法會先執行,再執行實現了BeanFactoryPostProcessor的postProcessBeanFactory。具體看調用順序圖

使用場景

  1. 修改現有的 BeanDefinition:可以在 Bean 實例化之前修改現有的 BeanDefinition,如更改其屬性值或作用域。
public class BeanDefinitionModifier implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        System.out.println("在 postProcessBeanDefinitionRegistry 中修改現有的 BeanDefinition");

        if (registry.containsBeanDefinition("myExistingBean")) {
            BeanDefinition beanDefinition = registry.getBeanDefinition("myExistingBean");
            MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
            propertyValues.add("propertyName", "newValue");
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 此方法可以留空或用於進一步處理
    }
}
  1. 條件性地註冊 Bean:基於某些條件(如環境變數、配置文件等)動態註冊或取消註冊某些 Bean。
public class ConditionalBeanRegistrar implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        System.out.println("在 postProcessBeanDefinitionRegistry 中根據條件註冊 Bean");

        if (someCondition()) {
            AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                    .genericBeanDefinition(ConditionalBean.class)
                    .getBeanDefinition();
            registry.registerBeanDefinition("conditionalBean", beanDefinition);
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 此方法可以留空或用於進一步處理
    }

    private boolean someCondition() {
        // 自定義條件邏輯
        return true;
    }
}
  1. 掃描和註冊自定義註解的 Bean:實現自定義註解的掃描邏輯,並動態註冊這些註解標註的 Bean。
public class CustomAnnotationBeanRegistrar implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        System.out.println("在 postProcessBeanDefinitionRegistry 中掃描並註冊自定義註解的 Bean");

        // 自定義掃描邏輯,假設找到一個類 MyAnnotatedBean
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                .genericBeanDefinition(MyAnnotatedBean.class)
                .getBeanDefinition();
        registry.registerBeanDefinition("myAnnotatedBean", beanDefinition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 此方法可以留空或用於進一步處理
    }
}
  1. 比如依賴Redis.jar,如果該依賴jar存在,則用redis當緩存,否則就用本地緩存。這個需求完全可以在postProcessBeanDefinitionRegistry中利用Class.forName判斷依賴,存在的話則註冊對應class到容器。
@Configuration
public class AppConfig {

    @Bean
    public static BeanDefinitionRegistryPostProcessor customBeanDefinitionRegistryPostProcessor() {
        return new BeanDefinitionRegistryPostProcessor() {

            @Override
            public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
                System.out.println("在 postProcessBeanDefinitionRegistry 中根據條件註冊緩存實現類");

                try {
                    // 檢查 Redis 依賴是否存在
                    Class.forName("redis.clients.jedis.Jedis");
                    System.out.println("檢測到 Redis 依賴,註冊 RedisCacheService");

                    AbstractBeanDefinition redisCacheBeanDefinition = BeanDefinitionBuilder
                            .genericBeanDefinition(RedisCacheService.class)
                            .getBeanDefinition();
                    registry.registerBeanDefinition("cacheService", redisCacheBeanDefinition);

                } catch (ClassNotFoundException e) {
                    System.out.println("未檢測到 Redis 依賴,註冊 LocalCacheService");

                    AbstractBeanDefinition localCacheBeanDefinition = BeanDefinitionBuilder
                            .genericBeanDefinition(LocalCacheService.class)
                            .getBeanDefinition();
                    registry.registerBeanDefinition("cacheService", localCacheBeanDefinition);
                }
            }

            @Override
            public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
                // 此方法可以留空或用於進一步處理
            }
        };
    }
}
  1. Mybatis就是用BeanFactoryPostProcessor去註冊的mapper

MapperScannerConfigurer 的主要功能是通過掃描指定的包路徑,找到所有標註了 @Mapper 註解(或其他指定註解)的介面,並將這些介面註冊為 Spring 的 BeanDefinition。這樣,Spring 容器在啟動時會自動創建這些 Mapper 介面的代理對象,並將其註入到需要的地方。

以下是 MapperScannerConfigurer 的核心代碼片段:

// org.mybatis.spring.mapper.MapperScannerConfigurer#postProcessBeanDefinitionRegistry
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware {

    private String basePackage;
    private ApplicationContext applicationContext;

    public void setBasePackage(String basePackage) {
        this.basePackage = basePackage;
    }

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

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
        //設置其資源載入器為當前的 ApplicationContext
        scanner.setResourceLoader(this.applicationContext);
        scanner.registerFilters();
        //調用 scanner.scan(this.basePackage) 方法,掃描指定的包路徑,找到所有符合條件的 Mapper 介面,並將它們註冊為 Spring 的 BeanDefinition。
        scanner.scan(this.basePackage);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 此方法可以留空或用於進一步處理
    }
}

BeanPostProcessor

org.springframework.beans.factory.config.BeanPostProcessor

介紹

BeanPostProcessor 介面定義了兩個基本的Bean初始化回調方法,在屬性賦值前後執行。

  • postProcessBeforeInitialization:在 Bean 初始化方法(如 @PostConstructInitializingBean.afterPropertiesSet 或自定義初始化方法)調用之前執行;返回的對象將是實際註入到容器中的 Bean,如果返回 null,則該 Bean 不會被註冊。可用於創建代理類
  • postProcessAfterInitialization:初始化bean之後,返回的對象將是實際註入到容器中的 Bean,如果返回 null,則該 Bean 不會被註冊。

使用場景

  1. 初始化前後進行自定義邏輯:在 Bean 初始化之前或之後執行一些自定義的操作,例如設置一些屬性、進行依賴註入、執行某些檢查等。
@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof MyBean) {
            System.out.println("bean初始化前: " + beanName);
            ((MyBean) bean).setName("Modified Name Before Initialization");
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof MyBean) {
            System.out.println("bean初始化後: " + beanName);
        }
        return bean;
    }
}

public class MyBean {
    private String name;

    public MyBean(String name) {
        this.name = name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void init() {
        System.out.println("bean正在init: " + name);
    }

    @Override
    public String toString() {
        return "MyBean{name='" + name + "'}";
    }

  1. 代理對象的生成:在 postProcessAfterInitialization 方法中生成 Bean 的代理對象,用於 AOP(面向切麵編程)或其他用途。
@Component
public class ProxyBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof MyBean) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(bean.getClass());
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                    System.out.println("Before method: " + method.getName());
                    Object result = proxy.invokeSuper(obj, args);
                    System.out.println("After method: " + method.getName());
                    return result;
                }
            });
            return enhancer.create();
        }
        return bean;
    }
}
  1. 日誌記錄和監控:記錄 Bean 的初始化過程,進行性能監控、日誌記錄等。
@Component
public class LoggingBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("開始初始化bean: " + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化bean結束: " + beanName);
        return bean;
    }
}

  1. 自動裝配和註入:在初始化前後進行自動裝配和註入,例如通過反射為某些欄位註入值。
@Component
public class AutowireBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Field[] fields = bean.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(AutowireCustom.class)) {
                field.setAccessible(true);
                try {
                    field.set(bean, "Injected Value");
                } catch (IllegalAccessException e) {
                    throw new BeansException("Failed to autowire field: " + field.getName(), e) {};
                }
            }
        }
        return bean;
    }

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

@Retention(RetentionPolicy.RUNTIME)
public @interface AutowireCustom {
}

public class MyBean {
    @AutowireCustom
    private String customField;

    public MyBean() {
    }

    @Override
    public String toString() {
        return "MyBean{customField='" + customField + "'}";
    }
}

InstantiationAwareBeanPostProcessor

org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor

介紹

該介面繼承了BeanPostProcessor介面,因為InstantiationAwareBeanPostProcessor也屬於Bean級的後置處理器,區別如下:

BeanPostProcess介面只在bean的初始化階段進行擴展(註入spring上下文前後),而InstantiationAwareBeanPostProcessor介面在此基礎上增加了3個方法,把可擴展的範圍增加了實例化階段和屬性註入階段。

該類主要的擴展點有以下6個方法,其中有兩個是BeanPostProcessor的擴展,主要在bean生命周期的兩大階段:實例化階段初始化階段,下麵一起進行說明,按調用順序為:

  • postProcessBeforeInstantiation:在Bean實例化之前調用,如果返回null,一切按照正常順序執行;如果返回的是一個實例的對象,那麼postProcessAfterInstantiation()會執行,其他的擴展點將不再觸發。
  • postProcessAfterInstantiation:在Bean實例化之後調用,可以對已實例化的Bean進行進一步的自定義處理。
  • postProcessPropertyValues(方法在spring5.1版本後就已棄用):bean已經實例化完成,在屬性註入時階段觸發,@Autowired@Resource等註解原理基於此方法實現;可以修改Bean的屬性值或進行其他自定義操作,當postProcessAfterInstantiation返回true才執行。
  • postProcessBeforeInitialization(BeanPostProcessor的擴展):初始化bean之前,相當於把bean註入spring上下文之前;可用於創建代理類,如果返回的不是 null(也就是返回的是一個代理類) ,那麼後續只會調用 postProcessAfterInitialization() 方法
  • postProcessAfterInitialization(BeanPostProcessor的擴展):初始化bean之後,相當於把bean註入spring上下文之後;返回值會影響 postProcessProperties() 是否執行,其中返回 false 的話,是不會執行。
  • postProcessProperties():在 Bean 設置屬性前調用;用於修改 bean 的屬性,如果返回值不為空,那麼會更改指定欄位的值

註意:InstantiationAwareBeanPostProcessor和 BeanPostProcessor 是可以同時被實現的,並且也會同時生效,但是InstantiationAwareBeanPostProcessor的執行時機要稍早於BeanPostProcessor;具體看上面調用順序圖

InstantiationAwareBeanPostProcessor 提供了更細粒度的控制,可以在 Bean 的實例化和屬性設置過程中插入自定義邏輯。無論是替換預設的實例化過程、控制依賴註入,還是修改屬性值,InstantiationAwareBeanPostProcessor 都提供了強大的靈活性和可擴展性,使得開發者可以在 Bean 的生命周期中進行更精細的控制。

使用場景

  1. 在實例化之前替換 Bean:替換預設的 Bean 實例化過程,可能是返回一個代理對象。
@Component
public class CustomInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if (beanClass == MyBean.class) {
            System.out.println("實例化之前替換 Bean: " + beanName);
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(beanClass);
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                    System.out.println("調用方法: " + method.getName());
                    return proxy.invokeSuper(obj, args);
                }
            });
            return enhancer.create();
        }
        return null;
    }
    
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化之後的 Bean: " + beanName);
        return bean;
    }
}
  1. 控制實例化後的依賴註入過程:在實例化後但在依賴註入之前進行一些自定義邏輯。
@Component
public class DependencyInjectionControlPostProcessor implements InstantiationAwareBeanPostProcessor {

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        if (bean instanceof MyBean) {
            System.out.println("實例化之後控制依賴註入: " + beanName);
            return false; // 不進行預設的依賴註入
        }
        return true;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化之後的 Bean: " + beanName);
        return bean;
    }
}
  1. 修改屬性值:在屬性值設置過程中進行干預,修改或添加屬性值。
@Component
public class PropertyModificationPostProcessor implements InstantiationAwareBeanPostProcessor {

    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {
        if (bean instanceof MyBean) {
            System.out.println("設置屬性值之前: " + beanName);
            // 修改屬性值的邏輯
        }
        return pvs;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化之後的 Bean: " + beanName);
        return bean;
    }
}

SmartInstantiationAwareBeanPostProcessor

org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor

介紹

SmartInstantiationAwareBeanPostProcessor 與其他擴展點最明顯的不同,就是在實際的業務開發場景中應用到的機會並不多,主要是在Spring內部應用。

該擴展介面有3個觸發點方法:

  • predictBeanType:該觸發點發生在postProcessBeforeInstantiation之前(也就是在 InstantiationAwareBeanPostProcessor的方法之前,在圖上並沒有標明,因為一般不太需要擴展這個點),這個方法用於預測Bean的類型,返回第一個預測成功的Class類型,如果不能預測,則返回null;當調用BeanFactory.getType(name)時當通過bean的名字無法得到bean類型信息時就調用該回調方法來決定類型信息。
  • determineCandidateConstructors:該觸發點發生在postProcessBeforeInstantiation之後,用於決定使用哪個構造器構造Bean,返回的是該bean的所有構造函數列表;如果不指定,預設為null,即bean的無參構造方法。用戶可以擴展這個點,來自定義選擇相應的構造器來實例化這個bean。
  • getEarlyBeanReference:該觸發點發生在postProcessAfterInstantiation之後,主要用於Spring迴圈依賴問題的解決,如果Spring中檢測不到迴圈依賴,這個方法不會被調用;當存在Spring迴圈依賴這種情況時,當bean實例化好之後,為了防止有迴圈依賴,會提前暴露回調方法,用於bean實例化的後置處理,會在InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation方法觸發執行之後執行;

註意:同InstantiationAwareBeanPostProcessor,由於SmartInstantiationAwareBeanPostProcessor 是 InstantiationAwareBeanPostProcessor的子類,因此也SmartInstantiationAwareBeanPostProcessor 也同樣能擴展 InstantiationAwareBeanPostProcessor的所有方法。但是如果有兩個類分別重寫了 SmartInstantiationAwareBeanPostProcessor 和 InstantiationAwareBeanPostProcessor 的方法,那麼重寫 InstantiationAwareBeanPostProcessor 的類的方法 會先於 重寫了 SmartInstantiationAwareBeanPostProcessor的類的方法(註意,這裡說的是兩者都有的方法)。

使用場景

  1. 自定義構造函數選擇:在實例化 Bean 時,選擇特定的構造函數。
@Component
public class CustomConstructorSelectionPostProcessor implements SmartInstantiationAwareBeanPostProcessor {

    @Override
    public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
        if (beanClass == MyBean.class) {
            System.out.println("選擇自定義構造函數: " + beanName);
            try {
                return new Constructor<?>[] { beanClass.getConstructor(String.class) };
            } catch (NoSuchMethodException e) {
                throw new BeansException("找不到指定的構造函數", e) {};
            }
        }
        return null;
    }
}

public class MyBean {
    private String name;

    public MyBean() {
        this.name = "Default Name";
    }

    public MyBean(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "MyBean{name='" + name + "'}";
    }
}
  1. 解決迴圈依賴問題:通過提供早期 Bean 引用,解決迴圈依賴問題。
@Component
public class EarlyBeanReferencePostProcessor implements SmartInstantiationAwareBeanPostProcessor {

    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        if (bean instanceof MyBean) {
            System.out.println("獲取早期 Bean 引用: " + beanName);
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(bean.getClass());
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                    System.out.println("調用方法: " + method.getName());
                    return proxy.invokeSuper(obj, args);
                }
            });
            return enhancer.create();
        }
        return bean;
    }
}
  1. 預測 Bean 類型:在 Bean 實例化之前,預測 Bean 的類型。
@Component
public class BeanTypePredictionPostProcessor implements SmartInstantiationAwareBeanPostProcessor {

    @Override
    public Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {
        if (beanClass == MyBean.class) {
            System.out.println("預測 Bean 類型: " + beanName);
            return MyBean.class;
        }
        return null;
    }
}

MergedBeanDefinitionPostProcessor

org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor

介紹

MergedBeanDefinitionPostProcessor 繼承自 BeanPostProcessor。調用的時機是,在實例化之後進行的調用,只要是收集bean上的屬性的,比如收集標記了某些註解的欄位或者方法,都可以基於MergedBeanDefinitionPostProcessor來進行擴展。

對於不同方式導入的Bean定義,如果存在重覆對同一個Bean的定義,則會根據allowBeanDefinitionOverriding屬性是否設置為true,判斷是否允許Bean定義的覆蓋,如果不允許,則拋出異常。而在Bean實例化之前,會進行BeanDefinition類型的歸一化,即 mergeBeanFintion ,統一轉換為RootBeanfintion進行後續處理。當然,這裡的merge更多指代的是父子Bean定義的合併。

也用於收集bean上的註解,比如常見的@Value、@NacosValue、@Mapper等,再將收集好的數據緩存在injectionMetadataCache中,以便後續比如屬性註入的時候使用。

該介面有兩個擴展方法:

  • postProcessMergedBeanDefinition:此方法在Spring將多個Bean定義合併為一個RootBeanDefinition之後,但在實例化Bean之前被調用。主要作用是讓開發者有機會在Bean定義合併後,對其進行進一步的定製和調整。使用場景如下:
    • 自定義註解處理:處理自定義註解並將其應用於Bean定義。
    • 屬性修改:在Bean實例化之前對Bean定義中的某些屬性進行調整或設置預設值。
  • resetBeanDefinition:此方法在Bean定義被重置時調用。它通常用於清理或重置與特定Bean定義相關的狀態或緩存。使用場景如下:
    • 狀態清理:清理緩存或臨時狀態,以便Bean定義可以被重新解析。
    • 重置自定義元數據:在Bean定義被重置時,重置自定義的元數據或狀態。

使用場景

  1. 對合併後的 Bean 定義信息進行修改:在 Bean 實例化之前,修改其定義信息,例如添加屬性值或修改構造函數參數。
@Component
public class CustomMergedBeanDefinitionPostProcessor implements MergedBeanDefinitionPostProcessor, BeanPostProcessor {

    @Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
        if (beanType == MyBean.class) {
            System.out.println("合併後的 Bean 定義信息, Bean 名稱: " + beanName);
            // 修改合併後的 Bean 定義信息
            beanDefinition.getPropertyValues().add("name", "修改後的名稱");
        }
    }

    @Override
    public void resetBeanDefinition(String beanName) {
        System.out.println("重置 Bean 定義信息, Bean 名稱: " + beanName);
        // 實現重置邏輯
    }

}

public class MyBean {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "MyBean{name='" + name + "'}";
    }
}

  1. 實現通用的自定義邏輯:在所有 Bean 實例化之前,執行一些通用的自定義邏輯。
@Component
public class CommonLogicMergedBeanDefinitionPostProcessor implements MergedBeanDefinitionPostProcessor, BeanPostProcessor {

    @Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
        System.out.println("處理合併後的 Bean 定義信息, Bean 名稱: " + beanName);
        // 添加通用的自定義邏輯,例如給所有 Bean 添加某個屬性
        beanDefinition.getPropertyValues().add("commonProperty", "通用屬性值");
    }

    @Override
    public void resetBeanDefinition(String beanName) {
        System.out.println("重置 Bean 定義信息, Bean 名稱: " + beanName);
        // 實現重置邏輯
    }

}

public class MyBean {
    private String name;
    private String commonProperty;

    public void setName(String name) {
        this.name = name;
    }

    public void setCommonProperty(String commonProperty) {
        this.commonProperty = commonProperty;
    }

    @Override
    public String toString() {
        return "MyBean{name='" + name + "', commonProperty='" + commonProperty + "'}";
    }
}
  1. 條件性地重置 Bean 定義信息:在某些條件下重置 Bean 的定義信息,使得下一次的實例化可以使用更新後的定義信息。
@Component
public class ConditionalResetMergedBeanDefinitionPostProcessor implements MergedBeanDefinitionPostProcessor, BeanPostProcessor {

    @Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
        System.out.println("處理合併後的 Bean 定義信息, Bean 名稱: " + beanName);
        // 這裡可以根據條件決定是否修改 Bean 定義
        if (beanName.equals("conditionalBean")) {
            beanDefinition.getPropertyValues().add("name", "重置後的名稱");
        }
    }

    @Override
    public void resetBeanDefinition(String beanName) {
        System.out.println("重置 Bean 定義信息, Bean 名稱: " + beanName);
        // 這裡可以實現條件性重置邏輯
    }
}

public class ConditionalBean {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "ConditionalBean{name='" + name + "'}";
    }
}

BeanNameAware

org.springframework.beans.factory.BeanNameAware

介紹

這個類是Aware擴展的一種,觸發點在bean的初始化之前,也就是postProcessBeforeInitialization之前,這個類的觸發點方法只有一個:setBeanName

用於讓 Bean 獲得其在 Spring 容器中的名稱。實現了 BeanNameAware 介面的 Bean 可以在初始化時獲得自身的 Bean 名稱,這在某些需要根據 Bean 名稱進行邏輯處理的場景非常有用。

使用場景

  1. 記錄或日誌輸出 Bean 名稱:在某些應用場景中,開發者可能希望在 Bean 初始化時記錄或輸出 Bean 的名稱。這對調試和日誌記錄非常有幫助。
@Component
public class LoggingBean implements BeanNameAware {

    private String beanName;

    @Override
    public void setBeanName(String name) {
        this.beanName = name;
        System.out.println("設置 Bean 名稱: " + name);
    }

    public void doSomething() {
        System.out.println("正在執行某些操作, 當前 Bean 名稱: " + beanName);
    }
}

@Configuration
@ComponentScan(basePackages = "com.seven")
public class AppConfig {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        LoggingBean loggingBean = context.getBean(LoggingBean.class);
        loggingBean.doSomething();
    }
}
  1. 根據 Bean 名稱實現條件性邏輯:有時,一個 Bean 可能需要根據其名稱決定執行不同的邏輯。例如,可以在初始化過程或某些方法調用中根據 Bean 名稱執行特定操作。
@Component
public class ConditionalLogicBean implements BeanNameAware {

    private String beanName;

    @Override
    public void setBeanName(String name) {
        this.beanName = name;
        System.out.println("設置 Bean 名稱: " + name);
    }

    public void performAction() {
        if ("conditionalLogicBean".equals(beanName)) {
            System.out.println("執行特定邏輯, 因為這是 conditionalLogicBean");
        } else {
            System.out.println("執行普通邏輯");
        }
    }
}

@Configuration
@ComponentScan(basePackages = "com.seven")
public class AppConfig {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        ConditionalLogicBean conditionalLogicBean = context.getBean(ConditionalLogicBean.class);
        conditionalLogicBean.performAction();
    }
}
  1. 動態註冊多個同類型的 Bean:在某些複雜的應用場景中,可能需要動態註冊多個同類型的 Bean,並且需要根據名稱區分它們。實現 BeanNameAware 介面可以很方便地獲取和使用這些 Bean 的名稱。
@Component("beanA")
public class DynamicBeanA implements BeanNameAware {

    private String beanName;

    @Override
    public void setBeanName(String name) {
        this.beanName = name;
        System.out.println("設置 Bean 名稱: " + name);
    }

    public void execute() {
        System.out.println("執行 Bean: " + beanName);
    }
}

@Component("beanB")
public class DynamicBeanB implements BeanNameAware {

    private String beanName;

    @Override
    public void setBeanName(String name) {
        this.beanName = name;
        System.out.println("設置 Bean 名稱: " + name);
    }

    public void execute() {
        System.out.println("執行 Bean: " + beanName);
    }
}

@Configuration
@ComponentScan(basePackages = "com.seven")
public class AppConfig {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        DynamicBeanA beanA = (DynamicBeanA) context.getBean("beanA");
        DynamicBeanB beanB = (DynamicBeanB) context.getBean("beanB");
        beanA.execute();
        beanB.execute();
    }
}

BeanClassLoaderAware

org.springframework.beans.factory.BeanClassLoaderAware

介紹

用於讓一個 Bean 獲取到載入它的 ClassLoader。實現這個介面的 Bean 會在其屬性設置完成後、初始化方法調用之前被註入 ClassLoader。該介面定義了一個方法:

  • void setBeanClassLoader(ClassLoader classLoader):在某些需要動態載入類的場景中,獲取 ClassLoader 是非常有用的。

使用場景

  1. 動態載入類:有時候,我們可能需要在運行時動態載入類,利用 BeanClassLoaderAware 可以方便地獲取到 ClassLoader 來實現這一需求。
@Component
public class DynamicClassLoader implements BeanClassLoaderAware {

    private ClassLoader classLoader;

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
        System.out.println("已設置類載入器");
    }

    public void loadClass(String className) {
        try {
            Class<?> clazz = classLoader.loadClass(className);
            System.out.println("已載入類:" + clazz.getName());
        } catch (ClassNotFoundException e) {
            System.out.println("類未找到:" + className);
        }
    }
}

@SpringBootApplication
public class AppConfig {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(AppConfig.class, args);
        DynamicClassLoader dynamicClassLoader = context.getBean(DynamicClassLoader.class);
        dynamicClassLoader.loadClass("java.util.ArrayList");
        dynamicClassLoader.loadClass("不存在的類");
    }
}
  1. 檢查類的可用性:在某些情況下,我們可能需要檢查某個類是否在當前的類路徑中可用。利用 BeanClassLoaderAware 可以方便地實現這一需求。
@Component
public class ClassAvailabilityChecker implements BeanClassLoaderAware {

    private ClassLoader classLoader;

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
        System.out.println("已設置類載入器");
    }

    public boolean isClassAvailable(String className) {
        try {
            Class<?> clazz = classLoader.loadClass(className);
            System.out.println("類可用:" + clazz.getName());
            return true;
        } catch (ClassNotFoundException e) {
            System.out.println("類不可用:" + className);
            return false;
        }
    }
}

@SpringBootApplication
public class AppConfig {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(AppConfig.class, args);
        ClassAvailabilityChecker checker = context.getBean(ClassAvailabilityChecker.class);
        checker.isClassAvailable("java.util.HashMap");
        checker.isClassAvailable("不存在的類");
    }
}
  1. 載入資源文件:通過 BeanClassLoaderAware 獲取的 ClassLoader,我們還可以方便地載入資源文件。
@Component
public class ResourceLoader implements BeanClassLoaderAware {

    private ClassLoader classLoader;

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
        System.out.println("已設置類載入器");
    }

    public void loadResource(String resourcePath) {
        InputStream inputStream = classLoader.getResourceAsStream(resourcePath);
        if (inputStream != null) {
            System.out.println("資源已載入:" + resourcePath);
        } else {
            System.out.println("資源未找到:" + resourcePath);
        }
    }
}

@SpringBootApplication
public class AppConfig {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(AppConfig.class, args);
        ResourceLoader resourceLoader = context.getBean(ResourceLoader.class);
        resourceLoader.loadResource("application.properties");
        resourceLoader.loadResource("不存在的資源");
    }
}

BeanFactoryAware

org.springframework.beans.factory.BeanFactoryAware

介紹

這個類只有一個觸發點,發生在bean的實例化之後,註入屬性之前,也就是Setter之前。這個類的擴展點方法為setBeanFactory,可以拿到BeanFactory這個屬性,從而能夠進行更複雜的 Bean 操作。例如,動態獲取其他 Bean、檢查 Bean 的狀態等。

使用場景

  1. 動態獲取其他 Bean:通過實現 BeanFactoryAware 介面,一個 Bean 可以在運行時動態獲取其他 Bean。這在一些需要解耦的場景下非常有用。
@Component
public class DynamicBeanFetcher implements BeanFactoryAware {

    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
        System.out.println("註入 BeanFactory 實例");
    }

    public void fetchAndUseBean() {
        MyBean myBean = beanFactory.getBean(MyBean.class);
        System.out.println("獲取到的 Bean 實例: " + myBean);
    }
}

@Component
public class MyBean {
    @Override
    public String toString() {
        return "這是 MyBean 實例";
    }
}

@Configuration
@ComponentScan(basePackages = "com.seven")
public class AppConfig {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        DynamicBeanFetcher fetcher = context.getBean(DynamicBeanFetcher.class);
        fetcher.fetchAndUseBean();
    }
}
  1. 檢查 Bean 的狀態:通過 BeanFactoryAware,可以在運行時檢查某個 Bean 是否存在或者其狀態,這對一些需要動態檢查 Bean 狀態的場景非常有用。
@Component
public class BeanStateChecker implements BeanFactoryAware {

    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
        System.out.println("註入 BeanFactory 實例");
    }

    public void checkBeanState() {
        boolean exists = beanFactory.containsBean("myBean");
        System.out.println("MyBean 是否存在: " + exists);
    }
}

@Component("myBean")
public class MyBean {
    @Override
    public String toString() {
        return "這是 MyBean 實例";
    }
}

@Configuration
@ComponentScan(basePackages = "com.seven")
public class AppConfig {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        BeanStateChecker checker = context.getBean(BeanStateChecker.class);
        checker.checkBeanState();
    }
}
  1. 創建複雜 Bean 的初始化邏輯:在一些複雜的業務場景中,有時需要在 Bean 初始化時執行一些複雜的邏輯,例如動態創建其他 Bean 並註入到當前 Bean 中。通過 BeanFactoryAware 可以實現這一點。
@Component
public class ComplexBeanInitializer implements BeanFactoryAware {

    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
        System.out.println("註入 BeanFactory 實例");
    }

    public void initializeComplexBean() {
        MyBean myBean = beanFactory.getBean(MyBean.class);
        System.out.println("初始化複雜 Bean, 獲取到的 MyBean 實例: " + myBean);
        // 在這裡可以執行複雜的初始化邏輯
    }
}

@Component
public class MyBean {
    @Override
    public String toString() {
        return "這是 MyBean 實例";
    }
}

@Configuration
@ComponentScan(basePackages = "com.seven")
public class AppConfig {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        ComplexBeanInitializer initializer = context.getBean(ComplexBeanInitializer.class);
        initializer.initializeComplexBean();
    }
}

ApplicationContextAwareProcessor

org.springframework.context.support.ApplicationContextAwareProcessor

介紹

該類本身並沒有擴展點,而是 BeanPostProcessor 擴展介面的具體實現,但是該類內部卻有6個擴展點可供實現 ,這些擴展點的觸發時機在bean實例化之後,初始化之前。

可以看到,該類用於執行各種驅動介面,在bean實例化之後,屬性填充之後。其內部有6個擴展點可供實現,這幾個介面都是Spring預留的重點擴展實現,與Spring的 Bean的生命周期 密切相關,以下按照擴展點調用順序介紹:

  • EnvironmentAware:用於獲取EnviromentAware的一個擴展類,這個變數非常有用, 可以獲得系統內的所有參數;另外也可以通過註入的方式來獲得Environment,用哪種方式需要以實現場景而決定。當然個人認為這個Aware沒必要去擴展,因為spring內部都可以通過註入的方式來直接獲得。

  • EmbeddedValueResolverAware:用於獲取StringValueResolver的一個擴展類, StringValueResolver可以獲取基於String類型的properties的變數;但一般我們都用@Value的方式來獲取properties的變數,用哪種方式需要以實現場景而決定。如果實現了這個Aware介面,把StringValueResolver緩存起來,通過這個類去獲取String類型的變數,效果是一樣的。

  • ResourceLoaderAware:用於獲取ResourceLoader的一個擴展類,ResourceLoader可以用於獲取classpath內所有的資源對象。

  • ApplicationEventPublisherAware:用於獲取ApplicationEventPublisher的一個擴展類,ApplicationEventPublisher可以用來發佈事件;這個對象也可以通過spring註入的方式來獲得,結合ApplicationListener來共同使用,下文在介紹ApplicationListener時會詳細提到。

  • MessageSourceAware:用於獲取MessageSource的一個擴展類,MessageSource主要用來做國際化。

  • ApplicationContextAware:用來獲取ApplicationContext的一個擴展類,ApplicationContext就是spring上下文管理器,可以手動的獲取任何在spring上下文註冊的bean。較多的做法是擴展這個介面來緩存spring上下文,包裝成靜態方法。
    同時ApplicationContext也實現了BeanFactoryMessageSourceApplicationEventPublisher等介面,也可以用來做相關介面的事情。

使用場景

  1. 動態獲取其他 Bean:通過實現 ApplicationContextAware 介面,Bean 可以在運行時動態獲取其他 Bean,這在一些需要解耦的場景下非常有用。
@Component
public class DynamicBeanFetcher implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        System.out.println("註入 ApplicationContext 實例");
    }

    public void fetchAndUseBean() {
        MyBean myBean = applicationContext.getBean(MyBean.class);
        System.out.println("獲取到的 Bean 實例: " + myBean);
    }
}

@Component
public class MyBean {
    @Override
    public String toString() {
        return "這是 MyBean 實例";
    }
}

@Configuration
@ComponentScan(basePackages = "com.seven")
public class AppConfig {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        DynamicBeanFetcher fetcher = context.getBean(DynamicBeanFetcher.class);
        fetcher.fetchAndUseBean();
    }
}
  1. 使用 ApplicationContext 進行事件發佈:在一些場景中,Bean 可能需要發佈事件。通過實現 ApplicationContextAware 介面,可以方便地獲取 ApplicationContext 實例併發布事件。
@Component
public class EventPublisherBean implements ApplicationContextAware, ApplicationEventPublisherAware {

    private ApplicationContext applicationContext;
    private ApplicationEventPublisher eventPublisher;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        System.out.println("註入 ApplicationContext 實例");
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    public void publishCustomEvent(String message) {
        CustomEvent customEvent = new CustomEvent(this, message);
        event

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Exercise 2.3 Implement a representation for rectangles in a plane. (Hint: You may want to make use of Exercise 2.2.) In terms of your constructors and ...
  • 預設配置,應用配置到可擴展配置 。 配置文件的體現方式是 XML 文件或者是 Properties 文 件 。現在 springboot 框架的配置框架數據參數的提現方式是 yml 文件和註解參數 。配置文 件是應用程式參數化解耦的一種方式。程式員項目組開發好應用程式的框架之後,遺留下來 的項目參數 ...
  • leetCode刷題筆記(9.2-9.9) 48.旋轉圖像(9.3) 1)圖像即二維數組,圖像的旋轉本質上是二維數組的旋轉變換 2)二維數組從外層來看,是若幹個子數組的集合,子數組內部維護各自的元素,即若幹個row里是row.length個column 3)由此可理解下麵幾個關於二維數組的函數: 創 ...
  • 跨平臺系列 cross-plateform 跨平臺應用程式-01-概覽 cross-plateform 跨平臺應用程式-02-有哪些主流技術棧? cross-plateform 跨平臺應用程式-03-如果只選擇一個框架,應該選擇哪一個? cross-plateform 跨平臺應用程式-04-Reac ...
  • 日常使用的移動手機或者是電腦等其它電子產品都是每天在產生不同的數據。數據安全性的 保證需要有很多的電腦程式設計的運行程式進行有效保證。電子產品是硬體設備,硬體設 備就像機器機械一樣是可以看得見摸得著的具體物件設施。物質和能量,源頭物料可以通過 不同的設計工廠設計師加工進行生產設計,開發形成不同的可 ...
  • Django 一、Django介紹 1.1 簡介 Django是python語言中的一個web框架,Python語言中主流的web框架有Django、Tornado、Flask 等多種。Django相較與其它WEB框架,其優勢為:大而全,框架本身集成了ORM、模型綁定、模板引擎、緩存、Session ...
  • 數據傳輸的過程首先要建立網路連接 。數據傳輸單元為數據包 DATA PRAGRAM. 電腦數 據網路的互通互聯物理硬體和軟體程式的管理。區域網絡是美國國防部連接不同電腦器設 備的一種方式 。光纜傳輸數據的速度更慢 。海底光纖的架設, 2000 年左右使得全球互聯網 時代惠國惠民。電腦信息技術起 ...
  • Python 速查表中文版 本手冊是 Python cheat sheet 的中文翻譯版。原作者:Arianne Colton and Sean Chen([email protected]) 編譯:ucasFL 目錄 常規 數值類類型 數據結構 函數 控制流 面向對象編程 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...