Spring的鉤子方法知多少

Spring是我們經常使用的一個框架,它功能之一是提供了我們管理bean對象的手段,而且它提供了很多鉤子方法給我們使用。什麼是鉤子方法呢?鉤子方法就是:在bean的生命週期之中,經歷了一系列的過程之中,Spring留給我們的一個後門,讓我們能在Spring的生命週期之中執行我們想要的方法,從而實現我們想要的功能。接下來我們介紹一下生命週期,然後一個一個介紹我見過的樣例或者我自己寫的樣例。

Spring生命週期的各種Aware

Spring的生命週期(我們撇開網絡上很多資料,因爲我覺得網絡上很多資料要麼不全,要麼分不清context和beanFactory,然後將context的一些過程歸入beanFactory之中。我這裏只看Spring官方文檔,我看的是5.3.0-SNAPSHOT版本的。)

在這裏插入圖片描述

上面列表說的114個初始化方法,13個銷燬方法。這就是我說的鉤子方法,只要你進程並實現了對應的方法,spring就會幫我們調用這些方法。

然後針對上面的bean lifecycle 我這裏用一個簡單的例子給大家演示一下:

public class BeanLifecycle implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, EnvironmentAware,
        EmbeddedValueResolverAware, ResourceLoaderAware, ApplicationEventPublisherAware, MessageSourceAware,
        ApplicationContextAware, ServletContextAware, BeanPostProcessor, InitializingBean, DestructionAwareBeanPostProcessor,
        DisposableBean{

    /**
     * BeanNameAware 的 setBeanName,這個是第一個觸發
     */
    @Override
    public void setBeanName(String s) {
        System.out.println("第一個執行 bean name = ");
    }


    /**
     * BeanClassLoaderAware 的 setBeanClassLoader
     */
    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        System.out.println("第二個執行  classLoader = ");
    }

    /**
     * BeanFactoryAware 的 setBeanFactory
     */
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("第三個執行 beanFactory = ");
    }

    /**
     * EnvironmentAware 的 setEnvironment
     */
    @Override
    public void setEnvironment(Environment environment) {
        System.out.println("第四個執行 environment = ");
    }

    /**
     * EmbeddedValueResolverAware 的 EmbeddedValueResolverAware
     */
    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        System.out.println("第五個執行 resolver = ");
    }

    /**
     * ResourceLoaderAware 的 setResourceLoader
     */
    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        System.out.println("第六個執行 resourceLoader = " );
    }

    /**
     * ApplicationEventPublisherAware 的 setApplicationEventPublisher
     */
    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        System.out.println("第七個執行 applicationEventPublisher = ");
    }

    /**
     * MessageSourceAware 的 setMessageSource
     */
    @Override
    public void setMessageSource(MessageSource messageSource) {
        System.out.println("第八個執行 messageSource = " );
    }

    /**
     * ApplicationContextAware 的 setApplicationContext
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("第九個執行 applicationContext = ");
    }

    /**
     * ServletContextAware 的 setServletContext
     */
    @Override
    public void setServletContext(ServletContext servletContext) {
        System.out.println("第十個執行 servletContext = " );
    }

    /**
     * BeanPostProcessor 的 postProcessBeforeInitialization
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("第十一個執行 postProcessBeforeInitialization bean = " );
        System.out.println("第十一個執行 postProcessBeforeInitialization beanName = " );
        return bean;
    }

    /**
     * InitializingBean 的 afterPropertiesSet
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("第十二個執行 = afterPropertiesSet");
    }

    /**
     * 指定 的 initMethod
     */
    public void initMethod() {
        System.out.println("第十三個執行 = initMethod");
    }

    /**
     * BeanPostProcessor 的 postProcessAfterInitialization
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("第十四個執行 postProcessAfterInitialization bean = " );
        System.out.println("第十四個執行 postProcessAfterInitialization beanName = " );
        return bean;
    }

    /**
     * DestructionAwareBeanPostProcessor 的 postProcessBeforeDestruction
     */
    @Override
    public void postProcessBeforeDestruction(Object o, String s) throws BeansException {
        System.out.println("第一個執行的銷燬方法 o = " );
        System.out.println("第一個執行的銷燬方法 s = " );
    }

    /**
     * DisposableBean 的 destroy
     */
    @Override
    public void destroy() throws Exception {
        System.out.println("第二個執行的銷燬方法 = destroy");
    }

    public void destroyMethod() {
        System.out.println("第三個執行的銷燬方法 ");
    }
}

然後我創建了下面一個測試用例:

@SpringBootApplication
public class LifecycleMain {

    @Bean(initMethod = "initMethod", destroyMethod = "destroyMethod")
    public BeanLifecycle beanLifecycle() {
        return new BeanLifecycle();
    }

    @Bean(initMethod = "initMethod", destroyMethod = "destroyMethod")
    public BeanLifecycle beanLifecycle2() {
        return new BeanLifecycle();
    }

    public static void main(String[] args) {
        ConfigurableApplicationContext context = new SpringApplication().run(LifecycleMain.class);
        context.close();  //這個方法用來觸發destroy-method
    }
}

output如下:

第一個執行 bean name = 
第二個執行  classLoader = 
第三個執行 beanFactory = 
第四個執行 environment = 
第五個執行 resolver = 
第六個執行 resourceLoader = 
第七個執行 applicationEventPublisher = 
第八個執行 messageSource = 
第九個執行 applicationContext = 
第十二個執行 = afterPropertiesSet
第十三個執行 = initMethod
第一個執行 bean name = 
第二個執行  classLoader = 
第三個執行 beanFactory = 
第四個執行 environment = 
第五個執行 resolver = 
第六個執行 resourceLoader = 
第七個執行 applicationEventPublisher = 
第八個執行 messageSource = 
第九個執行 applicationContext = 
第十二個執行 = afterPropertiesSet
第十三個執行 = initMethod
第十一個執行 bean = 
第十一個執行 beanName = 
第十一個執行 bean = 
第十一個執行 beanName = 
第十四個執行 bean = 
第十四個執行 beanName = 
第十四個執行 bean = 
第十四個執行 beanName = 
第二個執行的銷燬方法 = destroy
第三個執行的銷燬方法 
第二個執行的銷燬方法 = destroy
第三個執行的銷燬方法 

這裏我把後面輸出的對象都去掉了,因爲輸出結果太長影響我們觀看。

然後我先簡單說說Aware,Aware英文翻譯是知道的意思,Aware接口作用就是其實就是 你想知道什麼信息,然後就實現什麼樣的Aware,Spring會通過setXXX方法,將你想要知道的信息通過參數傳遞給你。

所以 BeanNameAware, BeanClassLoaderAware, BeanFactoryAware……的作用就是給對應的Bean回設這個Bean想知道的信息。

而這裏的有個小疑問:爲什麼第十個執行的Bean沒執行呢?而想象之中第十一個執行的方法在第十三個方法之後執行呢?

第一個問題是我們沒有對應的Servlet。

第二個問題:其實BeanPostProcessor是一個對所有Bean都會攔截的方法,如果我們想要在這個bean之前執行,我們需要在context的refresh階段之前添加進去而不是通過bean註冊進去,我們通過另外一種方式試試。

public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.getBeanFactory().addBeanPostProcessor(new BeanLifecycle());
    context.register(BeanLifecycle.class);
    context.refresh();
    context.getBean(BeanLifecycle.class);
}

output如下:

22:34:48.200 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
第十一個執行 postProcessBeforeInitialization bean = 
第十一個執行 postProcessBeforeInitialization beanName = 
第十四個執行 postProcessAfterInitialization bean = 
第十四個執行 postProcessAfterInitialization beanName = 
22:34:48.227 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
第十一個執行 postProcessBeforeInitialization bean = 
第十一個執行 postProcessBeforeInitialization beanName = 
第十四個執行 postProcessAfterInitialization bean = 
第十四個執行 postProcessAfterInitialization beanName = 
22:34:48.229 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
第十一個執行 postProcessBeforeInitialization bean = 
第十一個執行 postProcessBeforeInitialization beanName = 
第十四個執行 postProcessAfterInitialization bean = 
第十四個執行 postProcessAfterInitialization beanName = 
22:34:48.230 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
第十一個執行 postProcessBeforeInitialization bean = 
第十一個執行 postProcessBeforeInitialization beanName = 
第十四個執行 postProcessAfterInitialization bean = 
第十四個執行 postProcessAfterInitialization beanName = 
22:34:48.232 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
第十一個執行 postProcessBeforeInitialization bean = 
第十一個執行 postProcessBeforeInitialization beanName = 
第十四個執行 postProcessAfterInitialization bean = 
第十四個執行 postProcessAfterInitialization beanName = 
22:34:48.235 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'beanLifecycle'
第一個執行 bean name = 
第二個執行  classLoader = 
第三個執行 beanFactory = 
第十一個執行 postProcessBeforeInitialization bean = 
第十一個執行 postProcessBeforeInitialization beanName = 
第四個執行 environment = 
第五個執行 resolver = 
第六個執行 resourceLoader = 
第七個執行 applicationEventPublisher = 
第八個執行 messageSource = 
第九個執行 applicationContext = 
第十二個執行 = afterPropertiesSet
第十四個執行 postProcessAfterInitialization bean = 
第十四個執行 postProcessAfterInitialization beanName = 

爲什麼上面的postProcessAfterInitialization方法執行了好多次呢?

我們看看 BeanPostProcessor 的postProcessBeforeInitialization方法的註解:

Apply this BeanPostProcessor to the given new bean instance before any bean initialization callbacks (like InitializingBean’s afterPropertiesSet
or a custom init-method). The bean will already be populated with property values. The returned bean instance may be a wrapper around the original.

大意就是:這個方法會被每個bean 初始化之前都會回調這個方法,比如 會在InitializingBean’s afterPropertiesSet或bean自定義的一個init-method方法調用之前回調這個方法。

然後看看postProcessAfterInitialization 的註解:

Apply this BeanPostProcessor to the given new bean instance after any bean initialization callbacks (like InitializingBean’s afterPropertiesSet or a custom init-method). The bean will already be populated with property values. The returned bean instance may be a wrapper around the original.

大意就是:這個方法會在每個bean初始化之後都會回調這個方法

這樣就解釋了爲什麼的 postProcessBeforeInitializationpostProcessAfterInitialization 方法會被打印這麼多次,因爲它是每個bean被初始化一次,它都會被調用一次。那麼我設想是不是在需要對某些 bean,或者是底層接口的一個bean,我想攔截下來並且做一點修飾,這樣就不需要入侵到底層代碼裏面去了。

然後看了下 BeanPostProcessor 的註解:

Factory hook that allows for custom modification of new bean instances — for example, checking for marker interfaces or wrapping beans with proxies.

大意就是:Factory的hook(鉤子方法),可以定製修改一個新的bean實例,比如檢查interfaces的創建條件對不對,使用代理包裝某個beans。

不過上面有個結果比較奇怪:爲什麼的postProcessBeforeInitialization 會在 EnvironmentAware之前執行。這個挖個坑,後面地細細研究一下。

這裏給大家一個我使用BeanPostProcessor的例子。

@Configuration
public class MysqlConfigAdapter implements BeanPostProcessor{

    @Value("mysql.username")
    private String username;

    @Value("mysql.password")
    private String password;

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof XxxDatabase) {
            ((XxxDatabase)bean).setUsername(username);
            ((XxxDatabase)bean).setPassword(password);
        }
        return bean;
    }

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

上面的例子是本人在實際生產運用中使用到的例子(改造過),當時是因爲使用了第三方中間件,而爲了能讓第三方中間件接入配置中心(比如阿波羅,nacos),而不侵入對方的代碼,就在外層寫了個適配器(適配器模式)。攔截下對應的bean,並且修改裏面的值,再給出去。恰好符合註解講的那樣:The returned bean instance may be a wrapper around the original.

除了上面的例子之外,我們再說一點BeanPostProcessor的兩個注意事項:

  1. 註冊方式:BeanPostProcessor可以直接被ApplicationContext發現,也可以手動通過BeanFactory註冊。
  2. 順序:有多個BeanPostProcessor的時候,通過ApplicationContext發現的就會看這個bean是否implement了PriorityOrdered和Ordered接口,按照這兩個的順序來排序(PriorityOrdered優先於Ordered)。如果是通過手動註冊來的,就會忽略掉這些屬性,只看添加的順序來決定先後順序。最後@Order是無效的,目前還沒有實現(5.3.0-SNAPSHOT)版本。

BeanFactoryPostProcessor

PostProcessor 其實除了這個BeanPostProcessor 之外,之前看源碼的時候也注意到了有另外一個:BeanFactoryPostProcessor。然後我們看看BeanFactoryPostProcessor 源碼註解解釋。

Factory hook that allows for custom modification of an application context’s bean definitions, adapting the bean property values of the context’s underlying bean factory.

大意就是說:這是要給針對BeanFactory的鉤子方法,是一個可以對applicationContext的BeanFactory修改的方法。修改這個context的beanfactory的properties value。這個權限非常大。

  1. 它的註冊方式也分兩種:通過applicationContext自動探測,通過ConfigurableApplicationContext.addBeanFactoryPostProcessor手動註冊。
  2. 它的順序性和BeanPostProcessor一樣

其實BeanFactoryPostProcessor 是要給非常強大的接口,我們的@Configuration功能,SpringBoot的自動裝配還有Spring-Mybatis的適配都是靠這個來實現的。具體的實現可以參考ConfigurationClassPostProcessor ,如果後面有機會,可以再給大家看看ConfigurationClassPostProcessor它的實現。(再挖個坑。)

BeanFactoryPostProcessor 和BeanPostProcessor有個很大的區別是:BeanFactoryPostProcessor 的執行時機很早,在所有的bean初始化包括那些aware賦值之前就執行了,而BeanPostProcessor是每個bean初始化之前執行postProcessBeforeInitialization,初始化之後執行postProcessAfterInitialization。

好了,這期就到這裏了。

那麼我們把各種各樣的Aware看做一個階段,我們是否可以得出這樣的結論:

BeanFactoryPostProcessor.postProcessBeanFactory --> 
Aware --> 
BeanPostProcessor.postProcessBeforeInitialization --> 
initialBean --> 
initMethod -->
BeanPostProcessor.postProcessAfterInitialization --> 
DisposableBean --> 
destroyMethod

更多精彩內容歡迎關注我的微信公衆號:

在這裏插入圖片描述

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