<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
今天我們來說一說 Spring Bean 的生命週期,小夥伴們應該在面試中經常遇到,這是正常現象。因為 Spring Bean 的生命週期是除了 IoC、AOP 幾個核心概念之外最重要概念,大家務必拿下。可 Spring 原始碼又比較複雜,跟著跟著就不知道跟到哪裡去了,不太好拿下呀。這倒是真的,而且網上一上來就各種貼流程原始碼,對初學者來說是真的一臉懵逼,就像字都看的懂,但連在一塊就不知道意思了,太繞了。
我們講 Spring Bean 的生命週期之前先來了解兩個概念:
我們來看下 Spring Framework 的官方檔案:
In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container. Otherwise, a bean is simply one of many objects in your application. Beans, and the dependencies among them, are reflected in the configuration metadata used by a container.
簡而言之,bean 是由 Spring IoC 容器範例化、組裝和管理的物件。
對於普通的 Java 物件,當 new 的時候建立物件,然後該物件就能夠使用了。一旦該物件不再被使用,則由 Java 自動進行垃圾回收。
而 Spring 中的物件是 bean,bean 和普通的 Java 物件沒啥大的區別,只不過 Spring 不再自己去 new 物件了,而是由 IoC 容器去幫助我們範例化物件並且管理它,我們需要哪個物件,去問 IoC 容器要即可。IoC 其實就是解決物件之間的耦合問題,Spring Bean 的生命週期完全由容器控制。
這裡老周必須要提一下,這裡我們說的 Spring Bean 的生命週期主要指的是 singleton bean,對於 prototype 的 bean ,Spring 在建立好交給使用者之後則不會再管理後續的生命週期。
我們也來複習下 Spring 中的 bean 的作用域有哪些?
singleton
: 唯一 bean 範例,Spring 中的 bean 預設都是單例的。prototype
: 每次請求都會建立一個新的 bean 範例。request
: 每一次 HTTP 請求都會產生一個新的 bean,該 bean 僅在當前 HTTP request 內有效。session
: 每一次 HTTP 請求都會產生一個新的 bean,該 bean 僅在當前 HTTP session 內有效。global-session
: 全域性 session 作用域,僅僅在基於 Portlet 的 web 應用中才有意義,Spring5 已經沒有了。Portlet 是能夠生成語意程式碼(例如:HTML)片段的小型 Java Web 外掛。它們基於 portlet 容器,可以像 servlet 一樣處理 HTTP 請求。但是,與 servlet 不同,每個 portlet 都有不同的對談。
我們知道對於普通的 Java 物件來說,它們的生命週期就是:
而對於 Spring Bean 的生命週期來說:
範例化 -> 屬性賦值 -> 初始化 -> 銷燬
只有四個步驟,這樣拆解的話是不是感覺也不難?不像其他人寫的那樣直接一上來就各種 BeanPostProcessor、BeanFactoryPostProcessor 全部懟進流程裡去,別說讀者看著頭大,自己寫的可能短時間內還記得流程,隔個一段時間,你可能都不知道自己寫了個啥。
本來小編想通過 Bean 建立流程入口
AbstractApplicationContext#refresh() 方法的 finishBeanFactoryInitialization(beanFactory) 處帶大家跟一下原始碼,想了想還是不帶入過多的程式碼進來,直接給到最終的主要邏輯。
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { // 範例化階段 instanceWrapper = this.createBeanInstance(beanName, mbd, args); } ... Object exposedObject = bean; try { // 屬性賦值階段 this.populateBean(beanName, mbd, instanceWrapper); // 初始化階段 exposedObject = this.initializeBean(beanName, exposedObject, mbd); } catch (Throwable var18) { ... } ... }
至於銷燬,是在容器關閉時呼叫的,詳見 ConfigurableApplicationContext#close()
是不是很清爽了?至於 BeanPostProcessor、BeanFactoryPostProcessor 以及其他的類,在老周看來,只不過是對主流程四個步驟的一系列擴充套件點而已。
Spring Bean 的生命週期的擴充套件點超級多,老周這裡不可能全部列出來,只說核心的擴充套件點。這也就是為什麼 Spring 的擴充套件性很好的原因,開了很多的口子,儘可能讓某個功能高內聚鬆耦合,使用者需要哪個功能就用哪個,而不是直接來一個大而全的東西。
比如建構函式、getter/setter 以及 init-method 和 destory-method 所指定的方法等,也就對應著上文說的範例化 -> 屬性賦值 -> 初始化 -> 銷燬四個階段。
主要是後處理器方法,比如下圖的 InstantiationAwareBeanPostProcessor、BeanPostProcessor 介面方法。這些介面的實現類是獨立於 Bean 的,並且會註冊到 Spring 容器中。在 Spring 容器建立任何 Bean 的時候,這些後處理器都會發生作用。
我們翻一下原始碼發現 InstantiationAwareBeanPostProcessor 是繼承了 BeanPostProcessor
InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
呼叫點Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName)
返回值:如果返回的不為null,那麼後續的Bean的建立流程【範例化、初始化afterProperties】都不會執行,而是直接使用返回的快捷Bean,此時的正常執行順序如下:
InstantiationAwareBeanPostProcessor介面中的postProcessBeforeInstantiation,在範例化之前呼叫。
BeanPostProcessor介面中的postProcessAfterInitialization,在範例化之後呼叫。
總之,postProcessBeforeInstantiation 在 doCreateBean 之前呼叫,也就是在 bean 範例化之前呼叫的,英文原始碼註釋解釋道該方法的返回值會替換原本的 Bean 作為代理,這也是 AOP 等功能實現的關鍵點。
InstantiationAwareBeanPostProcessor#postProcessAfterInstantiation
呼叫點boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException
正常情況下在範例化之後在執行populateBean之前呼叫
返回值:如果有指定的bean的時候返回false,那麼後續的屬性填充和屬性依賴注入【populateBean】將不會執行,同時後續的postProcessPropertyValues將不會執行,但是初始化和BeanPostProcessor的仍然會執行。
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
範例化之後呼叫,在方法applyPropertyValues【屬性填充】之前
返回值:如果返回null,那麼將不會進行後續的屬性填充,比如依賴注入等,如果返回的pvs額外的新增了屬性,那麼後續會填充到該類對應的屬性中。
pvs:PropertyValues物件,用於封裝指定類的物件,簡單來說就是PropertyValue的集合,裡面相當於以key-value形式存放類的屬性和值。
pds:PropertyDescriptor物件陣列,PropertyDescriptor相當於儲存類的屬性,不過可以呼叫set,get方法設定和獲取對應屬性的值。
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
進入初始化介面:
我們先來看
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization
org.springframework.context.support.ApplicationContextAwareProcessor#postProcessBeforeInitialization
進入 invokeAwareInterfaces(bean);
方法,當前 bean 實現了 ApplicationContextAware 介面。
包括 AspectJWeavingEnabler、CustomAutowireConfigurer、ConfigurationClassPostProcessor 等。這些都是 Spring 框架中已經實現好的 BeanFactoryPostProcessor,用來實現某些特定的功能。
我們知道 Spring IoC 容器初始化的關鍵環節就在 org.springframework.context.support.AbstractApplicationContext#refresh 方法中 ,容器建立的主體流程都在這個方法裡面,這個方法是真的重要!!!
對於工廠後處理器方法老周這裡直接帶你看 invokeBeanFactoryPostProcessors(beanFactory); 方法,這個方法處理的是 BeanFactoryPostProcessor 介面的 Bean。呼叫方法如下:
跟到最重要的方法裡去,程式碼雖長,但邏輯中規中矩。
BeanFactoryPostProcessor
:一切處理 BeanFactory 的父介面BeanDefinitionRegistryPostProcessor
:實現了 BeanFactoryPostProcessor 介面的介面
流程說明:
可以理解為 Bean 類直接實現介面的方法,比如 BeanNameAware、BeanFactoryAware、ApplicationContextAware、InitializingBean、DisposableBean 等方法,這些方法只對當前 Bean 生效。
Aware 型別的介面的作用就是讓我們能夠拿到 Spring 容器中的一些資源。基本都能夠見名知意,Aware 之前的名字就是可以拿到什麼資源,例如 BeanNameAware 可以拿到 BeanName,以此類推。呼叫時機需要注意:所有的 Aware 方法都是在初始化階段之前呼叫的。
Aware 介面眾多,這裡同樣通過分類的方式幫助大家記憶。Aware 介面具體可以分為兩組,至於為什麼這麼分,詳見下面的原始碼分析。如下排列順序同樣也是 Aware 介面的執行順序,能夠見名知意的介面不再解釋。
Aware Group1
BeanNameAware
BeanClassLoaderAware
BeanFactoryAware
Aware Group2
EnvironmentAware
EmbeddedValueResolverAware
這個知道的人可能不多,實現該介面能夠獲取 Spring EL 解析器,使用者的自定義註解需要支援 SPEL 表示式的時候可以使用,非常方便。
ApplicationContextAware(ResourceLoaderAware/ApplicationEventPublisherAware/MessageSourceAware)
這幾個介面可能讓人有點懵,實際上這幾個介面可以一起記,其返回值實質上都是當前ApplicationContext 物件,因為 ApplicationContext 是一個複合介面,如下:
Aware 呼叫時機原始碼分析
可以看到並不是所有的 Aware 介面都使用同樣的方式呼叫。Bean××Aware 都是在程式碼中直接呼叫的,而 ApplicationContext 相關的 Aware 都是通過 BeanPostProcessor#postProcessBeforeInitialization() 實現的。感興趣的可以自己看一下 ApplicationContextAwareProcessor 這個類的原始碼,就是判斷當前建立的 Bean 是否實現了相關的 Aware 方法,如果實現了會呼叫回撥方法將資源傳遞給 Bean。
BeanPostProcessor 的呼叫時機也能在這裡體現,包圍住 invokeInitMethods 方法,也就說明了在初始化階段的前後執行。
關於 Aware 介面的執行順序,其實只需要記住第一組在第二組執行之前就行了。
至於剩下的兩個生命週期介面就很簡單了,範例化和屬性賦值都是 Spring 幫助我們做的,能夠自己實現的有初始化和銷燬兩個生命週期階段。
有一點需要注意,因為 Aware 方法都是執行在初始化方法之前,所以可以在初始化方法中放心大膽的使用 Aware 介面獲取的資源,這也是我們自定義擴充套件 Spring 的常用方式。
除了實現 InitializingBean 介面之外還能通過註解或者 xml 設定的方式指定初始化方法,至於這幾種定義方式的呼叫順序其實沒有必要記。因為這幾個方法對應的都是同一個生命週期,只是實現方式不同,我們一般只採用其中一種方式。
該介面只有一個方法 setBeanName(String name)
,用來獲取 bean 的id 或者 name
。
該介面只有一個方法 setBeanFactory(BeanFactory beanFactory)
,用來獲取當前環境中的 BeanFactory
。
該介面只有一個方法 setApplicationContext(ApplicationContext applicationContext)
,用來獲取當前環境中的 ApplicationContext
。
該介面只有一個方法 afterPropertiesSet()
,在屬性注入完成後呼叫
。
該介面只有一個方法 destroy()
,在容器銷燬的時候呼叫,在使用者指定的 destroy-method 之前呼叫
。
該介面有兩個方法:
該類是 BeanPostProcessor 的子介面,常用的有如下三個方法:
思路:建立一個類 UserBean ,讓其實現幾個特殊的介面,並分別在介面實現的構造器、介面方法中斷點,觀察執行緒呼叫棧,分析出 Bean 物件建立和管理關鍵點的觸發時機。
@Component public class UserBean implements InitializingBean, BeanNameAware, DisposableBean, ApplicationContextAware { private int id; private String name; public UserBean(int id, String name) { this.id = id; this.name = name; System.out.println("2. 呼叫建構函式"); } public int getId() { return id; } public void setId(int id) { this.id = id; System.out.println("5. 屬性注入 id"); } public String getName() { return name; } public void setName(String name) { this.name = name; System.out.println("5. 屬性注入 name"); } @Override public void setBeanName(String name) { System.out.println("6. 呼叫 BeanNameAware.setBeanName() 方法"); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { UserBean userBean = (UserBean) applicationContext.getBean("userBean"); System.out.println(userBean); System.out.println("7. 呼叫 BeanNameAware.setBeanName() 方法"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("9. 呼叫 InitializingBean.afterPropertiesSet() 方法"); } public void myInit() { System.out.println("10. 呼叫 init-method 方法"); } @Override public void destroy() throws Exception { System.out.println("12. 呼叫 DisposableBean.destroy() 方法"); } public void myDestroy() { System.out.println("13. 呼叫 destroy-method 方法"); } @Override public String toString() { return "UserBean{" + "id=" + id + ", name='" + name + ''' + '}'; } }
@Component public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor { @Override public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { if ("userBean".equals(beanName)) { System.out.println("1. 呼叫 InstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation() 方法"); } return null; } @Override public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { if ("userBean".equals(beanName)) { UserBean userBean = (UserBean) bean; System.out.println("3. 呼叫 InstantiationAwareBeanPostProcessor.postProcessAfterInstantiation() 方法"); System.out.println(userBean); } return true; } @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException { if ("userBean".equals(beanName)) { System.out.println("4. 呼叫 InstantiationAwareBeanPostProcessor.postProcessProperties() 方法"); } return null; } }
@Component public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if ("userBean".equals(beanName)) { System.out.println("8. 呼叫 BeanPostProcessor.postProcessBeforeInitialization() 方法"); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if ("userBean".equals(beanName)) { System.out.println("11. 呼叫 BeanPostProcessor.postProcessAfterInitialization() 方法"); } return bean; } }
@Component public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { System.out.println("0. 呼叫 BeanFactoryPostProcessor.postProcessBeanFactory() 方法"); } }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd "> <bean class="com.riemann.test.MyInstantiationAwareBeanPostProcessor" /> <bean id="userBean" class="com.riemann.test.UserBean" init-method="myInit" destroy-method="myDestroy"> <!-- 建構函式注入 --> <constructor-arg index="0" type="int"> <value>1</value> </constructor-arg> <constructor-arg index="1" type="java.lang.String"> <value>微信公眾號【老周聊架構】</value> </constructor-arg> <!-- setter方法注入 --> <property name="id" value="2"/> <property name="name" value="riemann"/> </bean> <bean class="com.riemann.test.MyBeanPostProcessor" /> <bean class="com.riemann.test.MyBeanFactoryPostProcessor" /> </beans>
public class BeanLifeCycleTest { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml"); UserBean user = (UserBean) applicationContext.getBean("userBean"); ((AbstractApplicationContext) applicationContext).close(); } }
以上就是一文讀懂 Spring Bean 的生命週期的詳細內容,更多關於Spring Bean生命週期的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45