聊透Spring bean的生命周期
在對于Spring的所有解讀中,Bean的生命周期都可謂是重中之重,甚至還有人稱Spring就是個管理Bean的容器。Bean的生命周期之所以這么重要,被反復提及,是因為Spring的核心能力,比如對象創(chuàng)建(IOC)、屬性注入(DI)、初始化方法的調用、代理對象的生成(AOP)等功能的實現(xiàn),都是在bean的生命周期中完成的。清楚了bean的生命周期,我們才能知道Spring的神奇魔法究竟是什么,是怎么一步步賦能,讓原本普通的java對象,最終變成擁有超能力的bean的。
1. bean的生命周期
Spring的生命周期大致分為:創(chuàng)建 -> 屬性填充 -> 初始化bean -> 使用 -> 銷毀 幾個核心階段。我們先來簡單了解一下這些階段所做的事情:
創(chuàng)建階段主要是創(chuàng)建對象,這里我們看到,對象的創(chuàng)建權交由Spring管理了,不再是我們手動new了,這也是IOC的概念。
屬性填充階段主要是進行依賴的注入,將當前對象依賴的bean對象,從Spring容器中找出來,然后填充到對應的屬性中去。
初始化bean階段做的事情相對比較復雜,包括回調各種Aware接口、回調各種初始化方法、生成AOP代理對象也在該階段進行,該階段主要是完成bean的初始化工作,后面我們慢慢分析。
使用bean階段,主要是bean創(chuàng)建完成,在程序運行期間,提供服務的階段。
銷毀bean階段,主要是容器關閉或停止服務,對bean進行銷毀處理。
當然,bean的生命周期中還包括其他的流程,比如合并beanDefinition、暴露工廠對象等,只是相對而言都是為其他功能做伏筆和準備的,在講到對應功能時,我們在做詳細分析。
1.1 創(chuàng)建bean
對象的創(chuàng)建是bean生命周期的第一步,畢竟要先有1才能有0嘛。創(chuàng)建對象的方式有很多,比如 new、反射、clone等等,Spring是怎么創(chuàng)建對象的呢?絕大多數情況下,Spring是通過反射來創(chuàng)建對象的,不過如果我們提供了Supplier或者工廠方法,Spring也會直接使用我們提供的創(chuàng)建方式。
我們秉持一貫的風格,從源碼出發(fā),看一下Spring是如何選擇創(chuàng)建方式的:
經過我們跟蹤源碼,發(fā)現(xiàn)Spring推斷創(chuàng)建方式還是比較聰明的,具體邏輯是:
- 先判斷是否提供了Supplier,如果提供,則通過Supplier產生對象。
- 再判斷是否提供工廠方法,如果提供,則使用工廠方法產生對象。
- 如果都沒提供,需要進行構造方法的推斷,具體邏輯為:
如果僅有一個構造方法,會直接使用該構造方法(如果構造方法有參數,會自動注入依賴參數)
如果有多個構造方法,會判斷有沒有加了@Autowired注解的構造方法:
如果沒有,Spring默認選擇無參構造方法;
如果有,且有@Autowired(required=true)的構造方法,就會選擇該構造方法;
如果有,但是沒有@Autowired(required=true)的構造方法,Spring會從所有加了@Autowired的構造方法中,根據構造器參數個數、類型匹配程度等綜合打分,選擇一個匹配參數最多,類型最準確的構造方法。
關于創(chuàng)建bean時,具體如何選擇構造方法的,本文我們不詳細展開。因為本文主旨在于分析bean的生命周期,我們只需要簡單理解為:Spring會選擇一個構造方法,然后通過反射創(chuàng)建出對象即可。其實在閱讀Spring源碼的時候,小伙伴們也一定要學會抓大放小,重點關注核心流程,細枝末節(jié)的地方可以先戰(zhàn)術性忽略,后續(xù)有需要時再回過頭分析也不遲,千萬不要陷進去,迷失了方向。
這里給感興趣的小伙伴附上一張流程圖,感興趣的小伙伴也可以留言,后續(xù)我們也可以單獨分析。
1.2 merged BeanDefinition
本階段是Spring提供的一個拓展點,通過MergedBeanDefinitionPostProcessor類型的后置處理器,可以對bean對應的BeanDefinition進行修改。Spring自身也充分利用該拓展點,做了很多初始化操作(并沒有修改BeanDefinition),比如查找標注了@Autowired、 @Resource、@PostConstruct、@PreDestory 的屬性和方法,方便后續(xù)進行屬性注入和初始化回調。當然,我們也可以自定義實現(xiàn),用來修改BeanDefinition信息或者我們需要的初始化操作,感興趣的小伙伴可以自行試一下哦。
1.3 暴露工廠對象
本階段主要是將早期bean對象提前放入到三級緩存singletonFactories中,為循環(huán)依賴做支持。在后續(xù)進行屬性填充時,如果發(fā)生循環(huán)依賴,可以從三級緩存中通過getObject()獲取該bean,完成循環(huán)依賴場景下的依賴注入。
該階段完全是為了支撐循環(huán)依賴的,是Spring為解決循環(huán)依賴埋的伏筆,在Bean的生命周期中完全可以忽略。這里為了完整性,和小伙伴們簡單提及一下。
如果對Spring如何解決循環(huán)依賴不是很清楚的話,可以看筆者的另一篇文章 聊透Spring循環(huán)依賴,詳細分析了Spring循環(huán)依賴的解決之道,對本階段的內容也有詳細的敘述。
1.4 屬性填充
本階段完成了Spring的核心功能之一:依賴注入,包括自動注入、@Autowired注入、@Resource注入等。Spring會根據bean的注入模型(默認不自動注入),選擇根據名稱自動注入還是根據類型自動注入。然后調用InstantiationAwareBeanPostProcessor#postProcessProperties()完成@Autowired和@Resource的屬性注入。
關于依賴注入,筆者在 聊透Spring依賴注入 中有詳細分析,不清楚的小伙伴可以先去感受一下Spring依賴注入的奇妙之處。
1.5 初始化bean
該階段主要做bean的初始化操作,包括:回調Aware接口、回調初始化方法、生成代理對象等。
- invokeAwareMethods():回調BeanNameAware、BeanClassLoaderAware、BeanFactoryAware感知接口。
- 回調后置處理器的前置方法,其中:
ApplicationContextAwareProcessor: 回調EnvironmentAware、ResourceLoaderAware、ApplicationContextAware、ApplicationEventPublisherAware、MessageSourceAware、EmbeddedValueResolverAware感知接口。
InitDestroyAnnotationBeanPostProcessor:回調標注了@PostConstruct的方法。
- invokeInitMethods()調用初始化方法:
如果bean是InitializingBean的子類, 先調用afterPropertiesSet()。
- 回調自定義的initMethod,比如通過@Bean(initMethod = "xxx")指定的初始化方法。
回調后置處理器的后置方法,可能返回代理對象。其中AbstractAutoProxyCreator和 AbstractAdvisingBeanPostProcessor都有可能產生代理對象,比如InfrastructureAdvisorAutoProxyCreator完成了@Transactional代理對象的生成,AsyncAnnotationBeanPostProcessor完成了@Async代理對象的生成。
在初始化完成后,bean會被放到單例池中,正式開始自己的使命:為項目服務,比如接收http請求,進行CRUD等等。后續(xù)有使用到該bean的地方,也是直接從單例池中獲取,不會再次創(chuàng)建bean(僅單例的哦)。
2. bean的來龍去脈
2.1 bean的掃描階段
現(xiàn)在我們已經知道Spring bean是如何創(chuàng)建的了,那什么時候創(chuàng)建這些bean呢,是遵循懶加載的思想,在實際使用的時候再創(chuàng)建嗎?其實不是的,因為bean之間的復雜關系和生命周期的原因,Spring在容器啟動的時候,就會實例化這些bean,然后放到單例池中,后續(xù)即用即取。并且在創(chuàng)建前、創(chuàng)建中、創(chuàng)建后都會做很多檢查,確保創(chuàng)建的bean是符合要求的,這些我們就不贅述了。
言歸正傳,細心的你一定發(fā)現(xiàn),創(chuàng)建bean時主要是從RootBeanDefinition mbd這個參數獲取bean的相關信息的,其實這就是大名鼎鼎的BeanDefinition,其中封裝了關于bean的元數據信息,關于BeanDefinition,后續(xù)我們會單獨講解,這里我們先理解為bean的元數據信息即可。那么這些元數據信息是什么時候解析的呢?
這就要提到Spring的類掃描了,其大致流程是:通過ASM字節(jié)碼技術掃描所有的類 -> 找出加了@Compont注解的(簡單理解) -> 封裝成BeanDefinition -> 存放到集合中。后續(xù)再實例化bean的時候,就可以遍歷這個集合,獲取到BeanDefinition,然后進行bean的創(chuàng)建了。
關于處理類掃描的ConfigurationClassPostProcessor后置處理器以及ConfigurationClassParser和ComponentScanAnnotationParser掃描器的具體細節(jié),后續(xù)我們單獨講解,和本章節(jié)關系不大,我們先簡單理解即可。
2.2 實例化后回調
?在前面的章節(jié)我們分析過:在容器中的bean實例化,放到單例池中之后,bean在創(chuàng)建階段的生命周期就正式完成,進入使用中階段,開啟對完服務之路。確實,這就是創(chuàng)建bean的全過程,如果有小伙伴看過筆者之前的聊Spring事件的那篇文章(聊透Spring事件機制),會發(fā)現(xiàn)對于@EventListener處理器的識別注冊,是在afterSingletonsInstantiated階段完成的。其實這里也是一個拓展點,我們完全可以實現(xiàn)SmartInitializingSingleton#afterSingletonsInstantiated(),在bean初始化完成后會回調該方法,進而觸發(fā)我們自己的業(yè)務邏輯,故這里我們單獨說一下。不清楚的小伙伴請移步先去了解一下哦。
2.3 bean的銷毀階段
?在創(chuàng)建bean的時候,會判斷如果bean是DisposableBean、AutoCloseable的子類,或者有destroy-method等,會注冊為可銷毀的bean,在容器關閉時,調用對應的方法進行bean的銷毀。