【SpringBoot】分析 SpringBoot 中的擴充套件點

酷酷-發表於2024-08-30

1 前言

SpringBoot 它給我們留了很多的擴充套件點,這節我們就看看都有哪些(有的擴充套件點是基於 Spring 的,有的我就不具體強調到底是 SpringBoot 還是 Spring 的噢)。

另外每一種擴充套件點我們都從兩個方面來看:

入口時機:入口就是 SpringBoot 解析或者尋找你自定義的類的時機

執行時機:就是 SpringBoot 執行的時機

2 ApplicationContextInitializer

位於spring-context包中的:

public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
    /**
     * Initialize the given application context.
     * @param applicationContext the application to configure
     */
    void initialize(C applicationContext);
}

2.1 入口時機

它的入口時機是在 SpringApplication 的例項化方法中:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    ...
    // 載入所有的初始化器
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    ...
}

原理:透過 SpringFactoriesLoader 載入檔案 META-INF/spring.factories 中 ApplicationContextInitializer 型別

2.2 執行時機

執行時機是在 SpringApplication 的 run 方法中的準備上下文 prepareContext 裡:

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
            SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    ...
    // 執行初始化
    applyInitializers(context);
    ...
}
protected void applyInitializers(ConfigurableApplicationContext context) {
    // 遍歷初始化器集合
    for (ApplicationContextInitializer initializer : getInitializers()) {
        // 檢查是否可以被呼叫
        Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
                ApplicationContextInitializer.class);
        Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
        // 執行每個初始化器 可以對上下文進行操作
        initializer.initialize(context);
    }
}

當有多個的話,是放到 List 容器中的,所以先放進來的先執行。

3 ApplicationListener

位於spring-context包中的:

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    /**
     * Handle an application event.
     * @param event the event to respond to
     */
    void onApplicationEvent(E event);
}

3.1 入口時機

入口時機我們這裡看兩種:

(1)定義在 META-INF/spring.factories

它的入口時機跟上邊的一樣的,也是在 SpringApplication 的例項化方法中:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    ...
    // 載入所有的監聽器
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    ...
}

原理:透過 SpringFactoriesLoader 載入檔案 META-INF/spring.factories 中 ApplicationListener 型別

(2)EventListener 註解

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EventListener {...}

可以看到這個註解是作用在方法上的,並且它的解析過程來源於,我這裡就不一點點帶大家看了,我畫了個圖:

首先是我們的註解型別的 ApplicationContext 在構造器中會注入註解相關的處理器,這裡就會涉及到放置預設的 EventListenerFactory (它會給我們的方法封裝一個 ApplicationListener 物件)、以及預設的 EventListenerMethodProcessor (它來解析我們的 Bean 中的 @EventListener 註解方法),兩者搭配幹活,最後透過 context 注入到監聽器集合並且也會往廣播器裡放一份(廣播器的初始化下邊也會看一下)

3.2 執行時機

它的執行是透過 ApplicationContext 釋出事件來做的,釋出事件裡會獲取到上下文中的廣播器 ApplicationEventMulticaster,然後廣播器裡獲取監聽器列表,然後順序執行(執行的時候有個小細節是廣播器裡有沒有配執行緒池,有的話就提交到執行緒池裡非同步執行)。

那我們在看執行時機之前,可能得先看下:

(1)廣播器的初始化

(2)監聽器的註冊或者來源(上邊註解形式的我們已經能看到註冊的過程,我們就看一下 spring.factories 中的監聽器的註冊)

3.2.1 廣播器的初始化

廣播器的初始化位於 Spring 的重新整理方法中,有一個 initApplicationEventMulticaster 即初始化廣播器方法,我們看看該方法:

// public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
protected void initApplicationEventMulticaster() {
    // 獲取 bean 工廠
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    // 判斷是否有使用者自定義的廣播器 applicationEventMulticaster
    if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
        // 有的話就用 使用者自定義的廣播器(比如當你需要非同步執行的時候,可以自己定義一個 Bean 設定下執行緒池)
        this.applicationEventMulticaster =
                beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
        if (logger.isTraceEnabled()) {
            logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
        }
    }
    else {
        // 沒有的話 就初始化一個預設的廣播器 SimpleApplicationEventMulticaster 並交給 Bean 工廠管理
        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
        if (logger.isTraceEnabled()) {
            logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
                    "[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
        }
    }
}

廣播器的類圖如下,捎帶了解一下:

3.2.2 監聽器的註冊

上邊註解形式的我們已經能看到註冊的過程,我們就看一下 spring.factories 中的監聽器是什麼放到廣播器或者 ApplicationContext 中的,這個就涉及到下邊的一個擴充套件點,大家可以先看一下下邊的擴充套件點 SpringApplicationRunListener,SpringBoot 在 spring.factories 放置了一個 EventPublishingRunListener,

在他的例項化方法中, 這傢伙還給自己內部定義了一個廣播器,並且把 spring.factories 中的 ApplicationListener 監聽器都放進來:

// private final SimpleApplicationEventMulticaster initialMulticaster;
/**
 * @param application SpringApplication
 * @param args 啟動引數
 */
public EventPublishingRunListener(SpringApplication application, String[] args) {
    this.application = application;
    this.args = args;
    // 初始化一個預設的事件廣播器
    this.initialMulticaster = new SimpleApplicationEventMulticaster();
    // 這裡會把 SpringApplication 的監聽器集合都放進廣播器裡
    for (ApplicationListener<?> listener : application.getListeners()) {
        this.initialMulticaster.addApplicationListener(listener);
    }
}

那麼它是什麼時候,把監聽器放到 ApplicationContext 中的呢?就在它的 contextLoaded 階段:

@Override
public void contextLoaded(ConfigurableApplicationContext context) {
    for (ApplicationListener<?> listener : this.application.getListeners()) {
        if (listener instanceof ApplicationContextAware) {
            ((ApplicationContextAware) listener).setApplicationContext(context);
        }
        // 放到 ApplicationContext 中
        context.addApplicationListener(listener);
    }
    // 透過預設的廣播器來廣播事件
    this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
}

有沒有有點小暈,EventPublishingRunListener 它也有廣播器了,那跟 Spring 的廣播器會不會多執行呢?其實不會,我們直接細看下:

看完上邊兩個,我們最後看下事件的釋出過程,一般我們透過 ApplicationContext 進行釋出,我這裡直接畫了個圖,一起看下:

事件釋出後,首先會統一把事件物件包裝成 ApplicationEvent 型別,然後會判斷 earlyApplicationEvents 屬性是否為空,他的值的變化有兩個觸發點:

(1)在 Spring 重新整理的時候,準備上下文的方法中,會首先對 earlyApplicationEvents 屬性進行初始化,來儲存一些提前釋出的事件

(2)在重新整理的後半段,會註冊監聽器,並註冊到廣播器裡,然後把 earlyApplicationEvents 裡邊暫存的事件撈出來並把 earlyApplicationEvents 設定為空,然後交給廣播器釋出

最後到達廣播器,裡邊有一個小細節就是執行緒池的設定(預設是沒有的)所以預設都是同步執行的。

對於執行順序,還是當監聽器有多個的話,是放到 List 容器中的,所以先放進來的先執行。

3.3 事件 Event

那我們順便看下 SpringBoot 中的一些常見事件:

首先是 EventPublishingRunListener 裡邊的 6個階段 + 1個失敗階段涉及到的7個事件:

(1)ApplicationStartingEvent SpringBoot 開始啟動階段(run方法的最前段)

(2)ApplicationEnvironmentPreparedEvent SpringBoot 準備環境階段

(3)ApplicationContextInitializedEvent SpringBoot 上下文初始化階段(這個時候ApplicationContext剛例項化好,進行初始化階段)

(4)ApplicationPreparedEvent SpringBoot 上下文就緒階段(初始化好了)

(5)ApplicationStartedEvent SpringBoot 這時候上下文都重新整理完了(就差 callRunners 沒執行即 CommandLineRunner、ApplicationRunner沒執行)

(6)ApplicationReadyEvent SpringBoot 服務啟動完了開始跑了

(7)ApplicationFailedEvent 失敗處理階段

捎帶再看一個:

WebServerInitializedEvent web容器初始化完畢事件 都是在 spring 重新整理上下文的最後 finishRefresh 裡釋出的

它的兩個子類:

ServletWebServerApplicationContext servlet容器型事件

ReactiveWebServerInitializedEvent 響應式容器事件

4 SpringApplicationRunListener

位於 SpringBoot 包裡的:

public interface SpringApplicationRunListener {
    ...6個階段
}

4.1 入口時機

它的入口時機是在 SpringApplication 的 run 方法中的前一段:

public ConfigurableApplicationContext run(String... args) {
    ...
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 監聽器的執行 starting
    listeners.starting();
    ...
}
private SpringApplicationRunListeners getRunListeners(String[] args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    // 也是透過該方法 getSpringFactoriesInstances 指定 SpringApplicationRunListener 型別來載入的
    return new SpringApplicationRunListeners(logger,
            getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}

SpringApplicationRunListeners 的內部的 listeners 就存放了所有的 SpringApplicationRunListener

原理:透過 SpringFactoriesLoader 載入檔案 META-INF/spring.factories 中 SpringApplicationRunListener 型別

4.2 執行時機

執行時機的話,SpringApplicationRunListener 總共分6個階段,外加一個失敗的處理階段(只有 starting、running 階段不會執行失敗處理,其他5個都會執行失敗處理),並且失敗處理不分,

比如有三個監聽器 A、B、C,假如A、B執行成功,C執行失敗的話,執行 failed 處理,A、B、C 三個都會執行,並不是只有 C 執行。

它的執行階段幾個時機,大家直接看圖,清晰明瞭:

當有多個的話,是放到 List 容器中的,所以先放進來的先執行。

5 SpringBootExceptionReporter

位於 SpringBoot 包裡的:

public interface SpringBootExceptionReporter {
    /**
     * Report a startup failure to the user.
     * @param failure the source failure
     * @return {@code true} if the failure was reported or {@code false} if default
     * reporting should occur.
     */
    boolean reportException(Throwable failure);
}

因為它的入口和執行銜接挺快,我們就一起看:

5.1 入口以及執行時機

public ConfigurableApplicationContext run(String... args) {
    ...
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    try {
        // 建立上下文
        context = createApplicationContext();
        // 入口時機:載入異常解析報告類
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                new Class[] { ConfigurableApplicationContext.class }, context);
        ...
    }catch (Throwable ex) {
        // 執行時機1:失敗處理 exceptionReporters
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        listeners.running(context);
    }
    catch (Throwable ex) {
        // 執行時機2:失敗處理 exceptionReporters
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}

再看看失敗處理:

private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception,
        Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) {
    try {
        try {
            ...
        }
        finally {
            reportFailure(exceptionReporters, exception);
            ...
        }
    }
    catch (Exception ex) {
        logger.warn("Unable to close ApplicationContext", ex);
    }
    ...
}
private void reportFailure(Collection<SpringBootExceptionReporter> exceptionReporters, Throwable failure) {
    try {
        // 遍歷執行 中間有一個拋錯或者 reportException 返回 false 後邊就不執行了
        for (SpringBootExceptionReporter reporter : exceptionReporters) {
            if (reporter.reportException(failure)) {
                registerLoggedException(failure);
                return;
            }
        }
    }
    ...
}

當有多個的話,是放到 List 容器中的,所以先放進來的先執行。

6 PostProcessor

在 Spring 中,存在多種型別的 PostProcessor,它們主要用於在 Spring 容器初始化bean的過程中提供擴充套件點。這塊可是一個大塊,硬骨頭,啟動過程中很多都是依賴後置處理器來完成的。

6.1 BeanFactoryPostProcessor

它主要是在 BeanFactory 載入 Bean 定義之後、例項化 Bean 之前對 Bean 的定義進行自定義修改和擴充套件,從而影響容器中的Bean配置。我本來是想把 BeanDefinitionRegistryPostProcessor 放第一個,但是它是繼承的 BeanFactoryPostProcessor,所以就拿來放第一個。

@FunctionalInterface
public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

6.1.1 入口時機

入口都是透過 AbstractApplicationContext 的 addBeanFactoryPostProcessor 方法新增進來的:

@Override
public void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor) {
    Assert.notNull(postProcessor, "BeanFactoryPostProcessor must not be null");
    this.beanFactoryPostProcessors.add(postProcessor);
}

比如 ConfigurationWarningsApplicationContextInitializer 它是實現了 ApplicationContextInitializer 初始化器,繼而拿到上下文物件放進來的。

public class ConfigurationWarningsApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    ...
    public void initialize(ConfigurableApplicationContext context) {
        context.addBeanFactoryPostProcessor(new ConfigurationWarningsApplicationContextInitializer.ConfigurationWarningsPostProcessor(this.getChecks()));
    }
    ...
}

6.1.2 執行時機

執行時機是在 AbstractApplicationContext 重新整理方法裡呼叫 invokeBeanFactoryPostProcessors(beanFactory);

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
    ...
}
// 獲取 BeanFactoryPostProcessors 其實就是直接返回 AbstractApplicationContext 中的 beanFactoryPostProcessors 集合
public List<BeanFactoryPostProcessor> getBeanFactoryPostProcessors() {
    return this.beanFactoryPostProcessors;
}

其中執行的這個方法裡還夾雜著 BeanDefinitionRegistryPostProcessor 的入口以及執行時機,我們下邊看。

6.2 BeanDefinitionRegistryPostProcessor

它主要是在容器的初始化過程中對 Bean 定義進行自定義處理,比如‌在Bean定義註冊之前和之後進行操作、允許在執行時註冊新的beans或修改現有的bean定義等。

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
    ...
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}

可以看到它繼承了 BeanFactoryPostProcessor。

6.2.1 入口以及執行時機

它的入口和執行是在一塊的,並且跟上邊的 BeanFactoryPostProcessor 執行時機都在一個裡邊,我們上邊看到它最後是在 PostProcessorRegistrationDelegate 的 invokeBeanFactoryPostProcessors 方法中執行的,一起看一下:

final class PostProcessorRegistrationDelegate {
    // 執行 BeanFactoryPostProcessor 處理器
    public static void invokeBeanFactoryPostProcessors(
            ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {

        Set<String> processedBeans = new HashSet<>();

        // 看這裡會涉及到 BeanDefinitionRegistryPostProcessor 的處理
        // 預設的 BeanFactory 是 DefaultListableBeanFactory 它是實現了 BeanDefinitionRegistry 所以基本都會走這裡
        if (beanFactory instanceof BeanDefinitionRegistry) {
            BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
            List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
            List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
            // 遍歷每個 BeanFactoryPostProcessor
            for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
                // 如果它還是 BeanDefinitionRegistryPostProcessor 的子類
                if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
                    BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor;
                    // 則順便執行 BeanDefinitionRegistryPostProcessor 的方法
                    registryProcessor.postProcessBeanDefinitionRegistry(registry);
                    // 並暫時先放到集合中
                    registryProcessors.add(registryProcessor);
                }
                else {
                    regularPostProcessors.add(postProcessor);
                }
            }

            List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

            // 看這裡就會從 Bean 工廠裡獲取 BeanDefinitionRegistryPostProcessor 型別的處理器
            String[] postProcessorNames =
                    beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            for (String ppName : postProcessorNames) {
                if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                    // 新增到集合中
                    currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                }
            }
            sortPostProcessors(currentRegistryProcessors, beanFactory);
            registryProcessors.addAll(currentRegistryProcessors);
            // 執行 BeanDefinitionRegistryPostProcessor 處理器
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());
            currentRegistryProcessors.clear();

            ...
        }
        // 等等下邊也還有很多分類執行的 就不一一看了
        ...
    }
}

至於人家為什麼融合到一塊,可能大概還是是其子類的關係吧。

6.3 BeanPostProcessor

它主要是在 Bean 初始化前後進行攔截操作,可以用來修改 Bean 的定義或例項,比如進行 AOP 代理的建立等。

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

}

6.3.1 入口時機

它的入口是在 AbstractApplicationContext 重新整理方法裡呼叫 registerBeanPostProcessors(beanFactory);

protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    // 委託 PostProcessorRegistrationDelegate 註冊 Bean 後置處理器
    PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
public static void registerBeanPostProcessors(
            ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
    ...
    // 獲取 BeanPostProcessor 例項中的所有名稱
    String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
    // 分類存放
    // 實現了PriorityOrdered介面
    List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
    // 實現了MergedBeanDefinitionPostProcessor介面
    List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
    // 實現了Ordered介面
    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);
        }
    }

    // First, register the BeanPostProcessors that implement PriorityOrdered.
    // 排序
    sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
    // 新增到Bean工廠
    registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
    ...
}
// BeanFactory 註冊 BeanPostProcessor
private static void registerBeanPostProcessors(
        ConfigurableListableBeanFactory beanFactory, List<BeanPostProcessor> postProcessors) {
    if (beanFactory instanceof AbstractBeanFactory) {
        ((AbstractBeanFactory) beanFactory).addBeanPostProcessors(postProcessors);
    }
    else {
        for (BeanPostProcessor postProcessor : postProcessors) {
            beanFactory.addBeanPostProcessor(postProcessor);
        }
    }
}
// 最後就到這裡先刪再加也就是後來的就排在最後 是放到 list 集合中
/** BeanPostProcessors to apply. */
// private final List<BeanPostProcessor> beanPostProcessors = new BeanPostProcessorCacheAwareList();
@Override
public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
    Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
    // Remove from old position, if any
    this.beanPostProcessors.remove(beanPostProcessor);
    // Add to end of list
    this.beanPostProcessors.add(beanPostProcessor);
}

6.3.2 執行時機

我們先看下該介面有兩個方法,分別是在:

(1)Bean 初始化之前:

在 Bean 初始化之前,BeanPostProcessor 的 postProcessBeforeInitialization 方法會被呼叫。

這個方法允許你在 Bean 初始化之前對 Bean 例項進行修改,例如新增或修改 Bean 的屬性。

(2)Bean 初始化之後:

在 Bean 初始化之後,BeanPostProcessor 的 postProcessAfterInitialization 方法會被呼叫。

這個方法允許你在 Bean 初始化之後對 Bean 例項進行修改,例如建立代理物件。

具體的執行流程:

(1)Bean 例項化:

當 Spring 容器建立一個新的 Bean 例項時,首先會呼叫建構函式或工廠方法來建立 Bean。在這個階段,BeanPostProcessor 不會被呼叫。

(2)依賴注入:

接下來,Spring 容器會對新建立的 Bean 進行依賴注入。在這個階段,BeanPostProcessor 也不會被呼叫。

(3)postProcessBeforeInitialization 呼叫:

當 Bean 的依賴注入完成後,但還沒有呼叫 Bean 的初始化方法(如果有定義的話)之前,Spring 會呼叫所有已註冊的 BeanPostProcessor 的 postProcessBeforeInitialization 方法。這個方法允許你修改 Bean 的例項,例如修改其屬性值。

(4)Bean 初始化:

如果 Bean 定義了初始化方法(例如透過 init-method 屬性或實現了 InitializingBean 介面 afterPropertiesSet方法),那麼 Spring 會呼叫相應的初始化方法。在這個階段,BeanPostProcessor 不會被呼叫。

(5)postProcessAfterInitialization 呼叫:

當 Bean 完成初始化後,Spring 會呼叫所有已註冊的 BeanPostProcessor 的 postProcessAfterInitialization 方法。這個方法允許你再次修改 Bean 的例項,例如建立代理物件。

這個的執行我們得回顧下 Bean 的生命週期過程,之前我在網上看到一個比較全的圖,我們看一下:

可以看到初始化前後的擴充套件點就是執行 BeanPostProcessor,並且有一個特殊的 InstantiationAwareBeanPostProcessor 它是在例項化的前後執行的(這個我有一篇之前是在想給每個 Feign 設定統一的 url 是用到過這個處理器來實現的,可以看看),也就是建立 Bean 物件例項的時候。

6.4 常見的 PostProcessor

6.4.1 MergedBeanDefinitionPostProcessor

首先它繼承了 BeanPostProcessor 那麼它可以在 Bean 初始化的前後執行擴充套件點,再看看它內部:

public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor {
    // 看名稱合併 BeanDefinition 可以理解為解析類的其他資訊合併到 Bean 定義資訊裡
    void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);

    default void resetBeanDefinition(String beanName) {
    }
}

它有幾個實現類,會涉及到下邊的一些擴充套件點的實現,我們提前透漏一下:

CommonAnnotationBeanPostProcessor:解析 @Resource

InitDestroyAnnotationBeanPostProcessor:解析 initAnnotationType(@PostConstruct) destroyAnnotationType(@PreDestroy) 這兩個屬性值的指定來源於 CommonAnnotationBeanPostProcessor

CommonAnnotationBeanPostProcessor 繼承了 InitDestroyAnnotationBeanPostProcessor:

public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor
        implements InstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable {
    public CommonAnnotationBeanPostProcessor() {
        ...
        setInitAnnotationType(PostConstruct.class);
        setDestroyAnnotationType(PreDestroy.class);
        ...
    }
}

AutowiredAnnotationBeanPostProcessor:解析 @Autowired @Value @Lookup

JmsListenerAnnotationBeanPostProcessor:解析 @JmsListener

ScheduledAnnotationBeanPostProcessor:解析 @Scheduled

另外再提一個跟 PostProcessor 不想關的 AnnotationConfigUtils:解析 @Lazy @Primary @DependsOn @Role @Description

6.4.2 ConfigurationClassPostProcessor

這個後置處理器那是相當牛逼,你看他解析的註解 :@Configuration @Component @Import @ComponentScan @Bean 都是響噹噹的。

這個註解真的,咱們這裡主要是看看擴充套件點,我以前的看原理的時候好幾個都是起源於這個處理器,在這裡我們就不細看了哈。

最後統一說下這些 PostProcessor 放到上下文或者Bean 工廠的時機:

在建立上下文物件的時候 AnnotationConfigApplicationContext:

public AnnotationConfigApplicationContext() {
    this.reader = new AnnotatedBeanDefinitionReader(this);
    this.scanner = new ClassPathBeanDefinitionScanner(this);
}
// 註解形式的讀寫器
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environm
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    Assert.notNull(environment, "Environment must not be null");
    this.registry = registry;
    this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
    // 註冊註解相關的處理器 都在這裡邊
    AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
        BeanDefinitionRegistry registry, @Nullable Object source) {
    ...
    Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);
    // ConfigurationClassPostProcessor
    if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
    // AutowiredAnnotationBeanPostProcessor
    if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
    // CommonAnnotationBeanPostProcessor
    if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
        def.setSource(source);
        beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
    // PersistenceAnnotationBeanPostProcessor
    if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
        ...
        beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
    }
    ...
    return beanDefs;
}

7 Aware

Aware 介面是一系列介面的集合,它們允許 Spring 容器向 Bean 提供有關其執行環境的附加資訊。這些介面通常用於讓 Bean 知道 Spring 容器中的某些特定上下文資訊,例如 Bean 工廠、應用上下文、資源載入器等。

public interface Aware {
}

(1)BeanFactoryAware:允許 Bean 訪問 BeanFactory。

這對於需要直接訪問 BeanFactory 的 Bean 特別有用,例如,當需要透過 BeanFactory 來獲取其他 Bean 的引用時。

(2)ApplicationContextAware:允許 Bean 訪問 ApplicationContext。

這對於需要訪問整個應用上下文的 Bean 特別有用,例如,當需要獲取其他 Bean 的引用或訪問應用上下文中的配置資訊時。

(3)EnvironmentAware:允許 Bean 訪問 Environment。

這對於需要訪問配置檔案或系統屬性的 Bean 特別有用。

(4)ResourceLoaderAware:允許 Bean 訪問 ResourceLoader。

這對於需要載入資原始檔的 Bean 特別有用。

(5)MessageSourceAware:允許 Bean 訪問 MessageSource。

這對於需要國際化支援的 Bean 特別有用,例如,當需要從訊息源中檢索訊息時。

(6)ApplicationEventPublisherAware:允許 Bean 訪問 ApplicationEventPublisher。

這對於需要釋出或訂閱應用事件的 Bean 特別有用。

(7)EmbeddedValueResolverAware:允許 Bean 訪問 EmbeddedValueResolver。

這對於需要解析表示式中的佔位符的 Bean 特別有用。

(8)WebApplicationContextAware:允許 Bean 訪問 WebApplicationContext。

這對於 Web 應用中的 Bean 特別有用,例如,當需要訪問 ServletContext 或其他 Web 上下文資訊時。

7.1 入口時機

入口的話,只要你的 Bean 能被 BeanFactory 管理就可以,下邊的執行時機會說。

7.2 執行時機

比如上邊的 8個 Aware,除了 BeanFactoryAware 其他的都是依託於 ApplicationContextAwareProcessor(實現了BeanPostProcessor)執行 Bean 初始化前擴充套件方法來注入的。

class ApplicationContextAwareProcessor implements BeanPostProcessor {
    private final ConfigurableApplicationContext applicationContext;

    public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
        this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory());
    }

    // Bean 初始化前擴充套件點方法
    @Override
    @Nullable
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        ...
        invokeAwareInterfaces(bean);
        return bean;
    }

    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 ApplicationStartupAware) {
            ((ApplicationStartupAware) bean).setApplicationStartup(this.applicationContext.getApplicationStartup());
        }
        if (bean instanceof ApplicationContextAware) {
            ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
        }
    }
}

那我們再順便看一下 ApplicationContextAwareProcessor 是怎麼來的,它是在 AbstractApplicationContext 重新整理 refresh 方法的時候,工廠建立出來後,有個 prepareBeanFactory 方法裡:

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 啟動步驟標記
        StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
        // 準備重新整理 Prepare this context for refreshing.
        prepareRefresh();
        // 通知子類重新整理內部bean工廠 Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // 準備工廠以便在此上下文中使用 Prepare the bean factory for use in this context.
        // 這個裡邊放入的
        prepareBeanFactory(beanFactory);
        ...
    }
}
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    ...
    // 新增Bean後置處理器  ApplicationContextAwareProcessor
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    ...
}

8 @PostConstruct、@PreDestory

由於他倆是同一個 InitDestroyAnnotationBeanPostProcessor 處理器來完成的,就放一塊來看了。

8.1 入口時機

我們上邊看過 CommonAnnotationBeanPostProcessor 是繼承了 InitDestroyAnnotationBeanPostProcessor,所以從 CommonAnnotationBeanPostProcessor 看起:

public CommonAnnotationBeanPostProcessor() {
    // 設定 InitDestroyAnnotationBeanPostProcessor 的兩個屬性值 告訴他解析哪兩個註解
    setInitAnnotationType(PostConstruct.class);
    setDestroyAnnotationType(PreDestroy.class);
    ignoreResourceType("javax.xml.ws.WebServiceContext");
}
InitDestroyAnnotationBeanPostProcessor 繼承了 MergedBeanDefinitionPostProcessor,那我們看下它的 postProcessMergedBeanDefinition 方法:
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
    // 找 @PostConstruct @PreDestroy 的註解資訊
    LifecycleMetadata metadata = findLifecycleMetadata(beanType);
    // 二次檢查
    metadata.checkConfigMembers(beanDefinition);
}
private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {
    // 為空說明沒解解析過 開始解析
    if (this.lifecycleMetadataCache == null) {
        return buildLifecycleMetadata(clazz);
    }
    // 下邊是直接走快取
    ...
    return metadata;
}
private LifecycleMetadata buildLifecycleMetadata(final Class<?> clazz) {
    // this.initAnnotationType @PostConstruct
    // this.destroyAnnotationType @PreDestory
    // 沒有這倆註解的話 直接返回預設的空資訊
    if (!AnnotationUtils.isCandidateClass(clazz, Arrays.asList(this.initAnnotationType, this.destroyAnnotationType))) {
        return this.emptyLifecycleMetadata;
    }
    // 分別存放兩種型別的集合
    List<LifecycleElement> initMethods = new ArrayList<>();
    List<LifecycleElement> destroyMethods = new ArrayList<>();
    Class<?> targetClass = clazz;
    do {
        final List<LifecycleElement> currInitMethods = new ArrayList<>();
        final List<LifecycleElement> currDestroyMethods = new ArrayList<>();
        ReflectionUtils.doWithLocalMethods(targetClass, method -> {
            // 判斷方法是由有 @PostConstruct
            if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) {
                LifecycleElement element = new LifecycleElement(method);
                // 有的話就新增進currInitMethods裡
                currInitMethods.add(element);
                if (logger.isTraceEnabled()) {
                    logger.trace("Found init method on class [" + clazz.getName() + "]: " + method);
                }
            }
            // 判斷方法是由有 @PreDestory
            if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) {
                // 有的話就新增進currDestroyMethods裡
                currDestroyMethods.add(new LifecycleElement(method));
                if (logger.isTraceEnabled()) {
                    logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method);
                }
            }
        });
        initMethods.addAll(0, currInitMethods);
        destroyMethods.addAll(currDestroyMethods);
        targetClass = targetClass.getSuperclass();
    }
    while (targetClass != null && targetClass != Object.class);
    return (initMethods.isEmpty() && destroyMethods.isEmpty() ? this.emptyLifecycleMetadata :
            new LifecycleMetadata(clazz, initMethods, destroyMethods));
}

解析完,我們最後會知道這個 Bean :

當它的類中沒有 @PostConstruct 和 @PreDestory 會返回一個預設空的 emptyLifecycleMetadata

有的話得到一個 LifecycleMetadata,initMethods 屬性裡存放了該類所有的 @ 方法,destroyMethods 存放了該類所有的 @ 方法;

那麼有了 LifecycleMetadata,還有一個二次檢查的方法 checkConfigMembers,我們繼續看看:

public void checkConfigMembers(RootBeanDefinition beanDefinition) {
    Set<LifecycleElement> checkedInitMethods = new LinkedHashSet<>(this.initMethods.size());
    // 遍歷 @PostConstruct 方法
    for (LifecycleElement element : this.initMethods) {
        String methodIdentifier = element.getIdentifier();
        if (!beanDefinition.isExternallyManagedInitMethod(methodIdentifier)) {
            // 放進了beanDefinition裡
            beanDefinition.registerExternallyManagedInitMethod(methodIdentifier);
            checkedInitMethods.add(element);
            if (logger.isTraceEnabled()) {
                logger.trace("Registered init method on class [" + this.targetClass.getName() + "]: " + methodIdentifier);
            }
        }
    }
    Set<LifecycleElement> checkedDestroyMethods = new LinkedHashSet<>(this.destroyMethods.size());
    for (LifecycleElement element : this.destroyMethods) {
        String methodIdentifier = element.getIdentifier();
        if (!beanDefinition.isExternallyManagedDestroyMethod(methodIdentifier)) {
            // 放進了beanDefinition裡
            beanDefinition.registerExternallyManagedDestroyMethod(methodIdentifier);
            checkedDestroyMethods.add(element);
            if (logger.isTraceEnabled()) {
                logger.trace("Registered destroy method on class [" + this.targetClass.getName() + "]: " + methodIdentifier);
            }
        }
    }
    // 最後我們的 checkedInitMethods 和 checkedDestroyMethods 裡會存放檢查後的方法
    this.checkedInitMethods = checkedInitMethods;
    this.checkedDestroyMethods = checkedDestroyMethods;
}

checkedInitMethods 和 initMethods 有啥區別麼?其實是為了給執行做準備,執行的話會優先取 checked裡的,當 checked 是空的話,取 initMethods。

至於為什麼這樣可能是不是跟別的有衝突,防止多次執行吧,我猜的。

8.2 執行時機

(1)@PostConstruct 的執行

InitDestroyAnnotationBeanPostProcessor 本身是個 BeanPostProcessor,所以它是在 Bean 初始化前的前置處理 postProcessBeforeInitialization 裡執行的,它又是來源於 Bean 生命週期的 doCreateBean 裡的 initializeBean方法裡執行的,

這個要熟悉 Bean 的生命週期過程中的一些常見方法比如 getBean->doGetBean->createBean->doCreateBean->createBeanInstance->populateBean->initializeBean,不熟悉的話可以看我前邊的文章哈。

那我們看看 postProcessBeforeInitialization:

// InitDestroyAnnotationBeanPostProcessor
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    // 這裡解析過會有快取 直接取快取的
    LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
    try {
        // 執行
        metadata.invokeInitMethods(bean, beanName);
    }
    ...
    return bean;
}
// LifecycleMetadata
public void invokeInitMethods(Object target, String beanName) throws Throwable {
    // 先取 checkedInitMethods
    Collection<LifecycleElement> checkedInitMethods = this.checkedInitMethods;
    // checkedInitMethods 為空的話取 initMethods
    Collection<LifecycleElement> initMethodsToIterate =
            (checkedInitMethods != null ? checkedInitMethods : this.initMethods);
    // 遍歷執行
    if (!initMethodsToIterate.isEmpty()) {
        for (LifecycleElement element : initMethodsToIterate) {
            // 執行
            element.invoke(target);
        }
    }
}

假如有多個方法的話,本身是按List儲存的,也沒有排序這種概念,所以從上到下解析,從類的前到後依次執行。

(2)@PreDestory 的執行

它的執行是 InitDestroyAnnotationBeanPostProcessor 繼承了 DestructionAwareBeanPostProcessor,也就是在銷燬某個 Bean 的時候執行。

這裡簡單說一下銷燬 Bean 都有哪些方式:

  • 容器管理的 Bean 銷燬:預設銷燬方法,當 Spring 容器關閉時,會自動呼叫所有單例(singleton)作用域的 Bean 的銷燬方法(如果定義了銷燬方法)。如果 Bean 實現了 DisposableBean 介面,Spring 會呼叫 destroy() 方法。如果 Bean 定義了 destroy-method 屬性,Spring 會呼叫指定的方法。顯式銷燬:可以透過 ApplicationContext 的 close() 方法顯式關閉 Spring 容器,從而觸發 Bean 的銷燬。
  • 程式設計式銷燬:手動銷燬:開發者可以透過呼叫 Bean 的銷燬方法來手動銷燬 Bean。例如,如果 Bean 實現了 DisposableBean 介面,可以手動呼叫 destroy() 方法。如果 Bean 定義了 destroy-method,可以手動呼叫該方法。
  • 原型作用域的 Bean 銷燬:每次請求銷燬:對於原型(prototype)作用域的 Bean,每次建立例項後,例項的生命週期由呼叫方管理。Spring 容器不會自動銷燬原型作用域的 Bean。如果需要銷燬,需要手動呼叫銷燬方法。
  • 外部系統銷燬:如果 Bean 的銷燬是由外部框架或容器管理的,那麼 Spring 容器不會呼叫銷燬方法。這種情況下,需要將 isExternallyManagedDestroyMethod 設定為 true。

最後我們看看 postProcessBeforeDestruction:

public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
    LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
    try {
        // 執行銷燬
        metadata.invokeDestroyMethods(bean, beanName);
    }
    ...
}
public void invokeDestroyMethods(Object target, String beanName) throws Throwable {
    // 先取 checkedDestroyMethods
    Collection<LifecycleElement> checkedDestroyMethods = this.checkedDestroyMethods;
    Collection<LifecycleElement> destroyMethodsToUse =
            (checkedDestroyMethods != null ? checkedDestroyMethods : this.destroyMethods);
    // 遍歷執行
    if (!destroyMethodsToUse.isEmpty()) {
        for (LifecycleElement element : destroyMethodsToUse) {
            // 執行
            element.invoke(target);
        }
    }
}

9 InitializingBean

它允許 Bean 在初始化完成後執行一些特定的操作。實現 InitializingBean 介面的目的是為了確保 Bean 在完全初始化之前執行一些必要的初始化邏輯,例如建立資料庫連線、載入配置資訊等。

public interface InitializingBean {
    void afterPropertiesSet() throws Exception;
}

9.1 入口時機

這個擴充套件不需要解析,只要你的 Bean 被 Bean 工廠管理就行。

9.2 執行時機

它的執行時機來源於 Bean 生命週期的 doCreateBean 裡的 initializeBean方法裡執行的:

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    ...
    invokeAwareMethods(beanName, bean);
    /*
     * 呼叫初始化方法:
     * 1. 若 bean 實現了 InitializingBean 介面,則呼叫 afterPropertiesSet 方法
     * 2. 若使用者配置了 bean 的 init-method 屬性,則呼叫使用者在配置中指定的方法
     */
    invokeInitMethods(beanName, wrappedBean, mbd);
    ...
}
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
        throws Throwable {
    // 看當前 Bean 是否實現了 InitializingBean 介面
    boolean isInitializingBean = (bean instanceof InitializingBean);
    // 是的話,並且不是交給外部管理的(沒見過外部管理的,一般都是交給 Bean 工廠管理)
    if (isInitializingBean && (mbd == null || !mbd.hasAnyExternallyManagedInitMethod("afterPropertiesSet"))) {
        ...
        // 呼叫afterPropertiesSet
        ((InitializingBean) bean).afterPropertiesSet();
    }
    // 如果還指定了 init-method 那麼執行一下該方法
    if (mbd != null && bean.getClass() != NullBean.class) {
        String initMethodName = mbd.getInitMethodName();
        if (StringUtils.hasLength(initMethodName) &&
                !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                !mbd.hasAnyExternallyManagedInitMethod(initMethodName)) {
            // 呼叫使用者自定義的方法
            invokeCustomInitMethod(beanName, bean, mbd);
        }
    }
}

10 DisposableBean

它允許 Bean 在容器銷燬時執行一些特定的操作。實現 DisposableBean 介面的目的是為了確保 Bean 在銷燬之前執行一些必要的清理操作,例如關閉資料庫連線、釋放資源等。

public interface DisposableBean {
    void destroy() throws Exception;        
}

10.1 入口時機

這個擴充套件不需要解析,只要你的 Bean 被 Bean 工廠管理就行。

10.2 執行時機

對於單例(singleton)作用域的 Bean,當 Spring 容器關閉時,會呼叫所有實現了 DisposableBean 介面的 Bean 的 destroy 方法。

當應用程式正常結束時,如果使用了 SpringApplication.run() 方法啟動應用,Spring Boot 會在應用結束時自動呼叫容器的 close() 方法,從而觸發 Bean 的銷燬。

對於原型(prototype)作用域的 Bean,Spring 容器不會自動呼叫 destroy 方法,因為每個請求都會建立一個新的 Bean 例項。如果需要在原型作用域的 Bean 銷燬時執行清理操作,需要手動呼叫 destroy 方法。

比如 SpringBoot 啟動的時候會在 refreshContext 重新整理上下文的時候,註冊關閉函式 context.registerShutdownHook();

@Override
public void registerShutdownHook() {
    if (this.shutdownHook == null) {
        // No shutdown hook registered yet.
        this.shutdownHook = new Thread() {
            @Override
            public void run() {
                synchronized (startupShutdownMonitor) {
                    doClose();
                }
            }
        };
        Runtime.getRuntime().addShutdownHook(this.shutdownHook);
    }
}
protected void doClose() {
    // Publish shutdown event.
    publishEvent(new ContextClosedEvent(this));
    // 生命週期的關閉
    this.lifecycleProcessor.onClose();
    // 銷燬 bean
    destroyBeans();
    ...
}
protected void destroyBeans() {
    getBeanFactory().destroySingletons();
}
public void destroySingletons() {
    ...
    String[] disposableBeanNames;
    synchronized (this.disposableBeans) {
        disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
    }
    // 遍歷執行銷燬
    for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
        destroySingleton(disposableBeanNames[i]);
    }
    ...
}

與 destroy-method 的區別:

DisposableBean 介面是 Spring 提供的一種標準方式,而 destroy-method 是透過配置指定的銷燬方法。

使用 DisposableBean 介面的好處在於它提供了一種標準的方式,而 destroy-method 則更加靈活,但可能缺乏一定的規範性。

與 InitializingBean 的關係:

DisposableBean 和 InitializingBean 分別用於銷燬和初始化操作,它們可以獨立使用,也可以同時在一個 Bean 上使用。

11 小結

暫時寫到這裡哈,想到新的擴充套件點的話,還有每個擴充套件點的一些常用操作後續再補充哈,有理解不對的地方歡迎指正哈。

相關文章