簡談Spring中bean的生命周期

先聊聊IOC容器

如果把IOC容器比作一個餐廳,那么BeanDefinition可以看作為原材料,容器中的每一個bean都會有一個對應的BeanDefinition實例,該實例負責保存bean對象的所有必要信息,包括bean對象的class類型、是否是抽象類、構(gòu)造方法和參數(shù)、其它屬性等等。BeanDefinitionRegistry和 BeanFactory就是這份菜譜,BeanDefinitionRegistry抽象出bean的注冊邏輯,而BeanFactory則抽象出了bean的管理邏輯,而各個BeanFactory的實現(xiàn)類就具體承擔了bean的注冊以及管理工作。DefaultListableBeanFactory作為一個比較通用的BeanFactory實現(xiàn),它同時也實現(xiàn)了BeanDefinitionRegistry接口,因此它就承擔了Bean的注冊管理工作。從圖中也可以看出,BeanFactory接口中主要包含getBean、containBean、getType、getAliases等管理bean的方法,而BeanDefinitionRegistry接口則包含registerBeanDefinition、removeBeanDefinition、getBeanDefinition等注冊管理BeanDefinition的方法。


類圖
下面聊聊spring的bean實例化過程,以現(xiàn)在流行的框架springboot為例。

Spring IoC容器的整個工作流程大致可以分為兩個階段:
1.容器啟動階段
容器啟動階段側(cè)重與bean對象管理信息的收集,一般bean有兩種定義方式,一種是定義在配置文件中,一種是通過注解方式定義。前者BeanDefinitionReader會對加載的 ConfigurationMetaData進行解析和分析,并將分析后的信息組裝為相應的BeanDefinition;或者通過ConfigurationClassPostProcessor等BeanFactoryPostProcessor解析注解抽象出BeanDefinition然后注冊到IOC容器。

2.Bean的實例化階段
經(jīng)歷了第一個階段,所有的Bean都通過BeanDefinition的方式注冊到BeanDefinitionRegistry,當某個請求通過容器的getBean方法請求某個對象,或者因為依賴關系容器需要隱式的調(diào)用getBean時,就會觸發(fā)第二階段的活動:容器會首先檢查所請求的對象之前是否已經(jīng)實例化完成。如果沒有,則會根據(jù)注冊的BeanDefinition所提供的信息實例化被請求對象,并為其注入依賴。當該對象裝配完畢后,容器會立即將其返回給請求方法使用。
BeanFactory作為IOC容器的基礎實現(xiàn),默認采用懶加載的模式,即只有訪問容器中的某個實例時,才會對其實例化。ApplicationContext建立在BeanFactory基礎之上,屬于高級容器,還會提供事件監(jiān)聽機制和國際化功能,而且它會在容器啟動時全部完成初始化和依賴注入操作。

bean生命周期和執(zhí)行順序.png

上面詳細展示一個bean從BeanDefinition到真正的實例經(jīng)過了哪些過程,了解這個過程有助于你在spring框架內(nèi)清楚執(zhí)行順序前提下更合理利用spring的各種擴展點。

在了解spring的bean生命周期后,來談談springboot提供的各種擴展點

1.BeanFactoryPostProcessor、BeanPostProcessor、DestructionAwareBeanPostProcessor
BeanFactoryPostProcessor主要作用就是在實例化前改變bean的定義,通過order來控制它們的執(zhí)行順序,它對應還有一個子類BeanDefinitionRegistryPostProcessor可以注冊添加自定義bean;BeanPostProcessor主要作用是spring完成bean實例化配置以及在其初始化前后加上自己的處理邏輯,同樣也是通過order來控制它們的執(zhí)行順序;DestructionAwareBeanPostProcessor主要作用是在對象銷毀前加上自己的處理邏輯。
舉例說明
PropertyPlaceholderConfigurer作為一個BeanFactoryPostProcessor,當BeanFactory在第一階段加載完所有配置信息時,BeanFactory中保存的對象的屬性還是以占位符方式存在的,比如 ${jdbc.mysql.url};當PropertyPlaceholderConfigurer作為一個BeanFactory會在第二階段解析占位符完成替換為實際的配置文件字符串功能。

2.bean級的接口
包括:BeanFactoryAware、BeanNameAware、InitializingBean、DiposableBean、FactoryBean這些接口方法都是在實例化對象后調(diào)用的。
這邊主要聊聊FactoryBean存在的意義,factoryBean本質(zhì)是bean,但是它和一般的bean不同之處它的存在會為了裝飾創(chuàng)建bean。當你創(chuàng)建的bean很復雜的時候,或者需要動態(tài)創(chuàng)建的時候,factoryBean就有它的用處了。我在實現(xiàn)自定義衰減隊列時也用到了這個,當時我有一個場景是對于每一個目標衰減的實現(xiàn)類都要創(chuàng)建一個對應的監(jiān)聽消息的處理類,這個時候我用到了FactoryBean,代碼如下

public class HandlerAnnotationFactoryBean implements FactoryBean<DefaultBestNotify> {

    @Getter
    @Setter
    private Class<NotifyHandler> innerClass;

    @Getter
    @Setter
    private ApplicationContext applicationContext;

    @Getter
    @Setter
    private String tagPrefix;
    @Override
    public DefaultBestNotify getObject() {
        return new DefaultBestNotify(innerClass, applicationContext, tagPrefix);
    }

    @Override
    public Class<?> getObjectType() {
        return DefaultBestNotify.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}
AbstractBeanDefinition bd = BeanDefinitionBuilder
                .rootBeanDefinition(HandlerAnnotationFactoryBean.class)
                .addPropertyValue("innerClass", beanClass)
                .addPropertyValue("applicationContext", applicationContext)
                .addPropertyValue("tagPrefix", tagPrefix)
                .getBeanDefinition();

        registry.registerBeanDefinition(Constants.PREFIX_BEST_BEAN + handler.name(), bd);

還有其他典型的例子
mybatis的MapperFactoryBean獲取mapper接口實例對象
mybatis的SqlSessionFactoryBean通過buildSqlSessionFactory獲取sqlSessionFactory實例對象dubbo的ReferenceBean獲取refrence接口實例對象
spring-boot通過PropertiesConfigurationFactory的bindPropertiesToTarget綁定配置屬性到對象

3.bean自身的方法
包括:<Bean>定義init_method、destrory_method

4. ApplicationListener、ApplicationContextInitializer
ApplicationListener監(jiān)聽事件通知,通過這個擴展點,程序可以實現(xiàn)事件分發(fā)處理模型

@Component
public class DemoListener {
    @Async(value = "taskExecutor")
    @EventListener
    public void process(DemoEvent demoEvent) {
        // 發(fā)短信通知
        switch (target.getType()) {
            case 1:
                // 邏輯1
                break;
            case 2:
                //邏輯2
                break;
            case 3:
                //邏輯3
                break;
          }
        }
    }
}

ApplicationContextInitializer允許我們對項目初始化時添加一些自定義的實現(xiàn),一般很少使用。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容

  • 2.1 我們的理念是:讓別人為你服務 IoC是隨著近年來輕量級容器(Lightweight Container)的...
    好好學習Sun閱讀 2,733評論 0 11
  • Spring容器高層視圖 Spring 啟動時讀取應用程序提供的Bean配置信息,并在Spring容器中生成一份相...
    Theriseof閱讀 2,838評論 1 24
  • 1.Spring整體架構(gòu) 1)核心容器(Core Container) Core模塊,主要包含了Spring框架基...
    Sponge1128閱讀 1,091評論 0 1
  • 敬愛的李總,親愛的家人們: 大家好! 今天是2019年1月13日,我的日精進第34天!我們互相勉勵,攜手前行,每...
    鑫和任艷閱讀 283評論 0 0
  • 你上學時通常是收情書者還是寫情書的人? 你收到過很多情書嗎? 你收過讓自己心跳的情書嗎?然后呢? 給你印象最深的是...
    清晨的玉蘭閱讀 219評論 0 1