可以硬剛面試官的:Spring IOC詳解 以及 Bean生命週期詳細過程

可以硬剛面試官的:Spring IOC詳解 以及 Bean生命週期詳細過程

推薦閱讀

這可能是全網Java學習路線最完整,最詳細的版本了,沒有之一

面試題:

來自面試官發自靈魂深處的拷問:談談你對spring的理解;

一臉懵逼的求職者的內心活動:啥? 具體的問題是什麼?現在的面試都不按套路出牌了嗎? 丟擲一個這麼大的問題,你讓我怎麼回答?

為什麼面試官要問這種問題?

不可否認,現在的大多數的面試出題方式都是這樣的,驚人的相似,就是面試官喜歡丟擲一個問題,看你能講多深,考的就是你對這項技術的深度和廣度,深度就是你對技術底層瞭解程度,廣度就是這項技術的應用範圍,以及擴充套件方向。這時候一個槓精同學就要發問了:“為什麼要知道這些那麼底層的東西呢?我只要會用不就行了,總是面試的時候造火箭,實際工作的時候卻讓我擰螺絲”,話雖說的沒錯,但你需要考慮的是,這個東西大家都會用,又不只你會,既然大家都會的東西,如果體現出你的價值呢?這就需要考慮到深度了,小紅和小明都會用spring,但是小明知道它底層的執行機制和原理,這裡需要注意的是,你回答的深度就決定了你薪資和待遇,這是非常重要的,那我們要如何回答這種問題呢?其實很簡單,先梳理出大概的脈絡,然後一個個地深入講解;我從今年寫的部落格都是這樣的,由淺入深地細化講解,並且會給出程式碼和流程圖;

作為一名程式設計師,如果想要向專家方向發展,就必須懂的程式設計的思想,演算法的底層執行原理,程式碼只是其次,因為原理就一種,實現方式多種多樣,一個介面,讓一萬個人去寫實現,這一萬個人的實現方式都不會一樣;這就是思想的重要性;

Spring IOC

首先,在此之前,我們就必須先知道什麼是ioc,ioc叫做控制反轉,也可以稱為依賴注入(DI),實際上依賴注入是ioc的另一種說法,

1。誰控制誰?

:在以前,物件的建立和銷燬都是由使用者控制的,用了ioc之後,物件的建立和銷燬就都交給容器來控制了,使用者就不用管這些,只關注業務需求就好了;

可以硬剛面試官的:Spring IOC詳解 以及 Bean生命週期詳細過程

2。什麼是反轉?

:既然叫反轉,肯定就有正轉,正轉其實就是物件去找例項,而反轉就反過來了嘛,讓例項來找物件;怎麼找呢?當然是透過容器啦!

可以硬剛面試官的:Spring IOC詳解 以及 Bean生命週期詳細過程

3。誰依賴誰?

:在spring專案中,將物件理解為Bean,也可以叫bean物件,這個bean和容器之間有個依賴關係,bean物件的建立是依賴容器的,就好像孩子依賴父母一樣,孩子不能自己生出自己,需要父母的合作才能出生,這裡的孩子就是bean,父母就是容器;

可以硬剛面試官的:Spring IOC詳解 以及 Bean生命週期詳細過程

4。誰注入誰?

:透過容器注入了bean物件,而且這個過程是自動化的,也就是說容器會自動找到和bean物件匹配的型別例項注入到物件中;

可以硬剛面試官的:Spring IOC詳解 以及 Bean生命週期詳細過程

spring ioc的載入過程

瞭解完控制反轉和依賴注入,接下來我們在看看ioc的載入過程,ioc的整個載入過程如下圖,先看看大致的流程,然後再慢慢深入 (

其中黃色的框內是註釋內容

可以硬剛面試官的:Spring IOC詳解 以及 Bean生命週期詳細過程

1、首先,透過BeanDefinitionReader 讀取指定的配置檔案生成bean的定義資訊,然後到完整的bean定義資訊(BeanDefinition物件),注意這裡只是儲存bean的定義資訊,還沒有例項化bean物件;就像工廠裡面一樣,原材料已經準備好了,但是還沒有進行生產,原材料就是beanDefinition,生產就是例項化

2、在 BeanDefinition 和 完整BeanDefinition 中間透過一個後置增強器,可以對bean的定義資訊進行統一修改,只需要實現 BeanFactoryPostProcessor 介面即可,這個後置增強器是可以有多個的,你只要在不同的類實現多個 BeanFactoryPostProcessor 介面就會執行多次,就像這樣:

package com。Spring。Boot。init;import org。springframework。beans。BeansException;import org。springframework。beans。factory。config。BeanFactoryPostProcessor;import org。springframework。beans。factory。config。ConfigurableListableBeanFactory;import org。springframework。stereotype。Component;/** * 擴充套件方法——後置增強器(可修改bean的定義資訊) */@Componentpublic class ExtBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {// BeanDefinition studentService = beanFactory。getBeanDefinition(“studentService”); System。out。println(“擴充套件方法——可進行修改beanDefinition的定義資訊”); }}

3、得到 完整BeanDefinition 之後就可以進行建立物件了,這整個過程被稱為 bean 的生命週期,也就是從例項化到銷燬的過程;那麼這時候愛學習童鞋就要發問了:“物件建立和銷燬有這麼麻煩嘛?直接反射例項化一個物件不就行了嘛?為啥還有初始化?”;首先,這是個好問題,來,我們先把掌聲送給這位發問的同學;我想說的是,就算是普通的new一個物件出來,裡面也會經過例項化和初始化,有興趣的話請看我的另一篇文章 :java建立物件過程 例項化和初始化;接下來我們重點講bean的生命週期;

Spring Bean的生命週期

粗略來看,bean的生命週期主要分為以下4個步驟

可以硬剛面試官的:Spring IOC詳解 以及 Bean生命週期詳細過程

但其實,它的內部蘊含了很多東西,讓我們看看細化後的流程圖;

可以硬剛面試官的:Spring IOC詳解 以及 Bean生命週期詳細過程

怎麼樣?是不是看到了很多沒見過的東西?好像認識幾個,但大多都是沒見過的東東,不知道不要緊,接下來我們一個個地講解

接下來我們要將1、3、4 放到一起講,是因為它們是在同一個接口裡面的,實現

InstantiationAwareBeanPostProcessor

介面即可

package com。Spring。Boot。init; import org。springframework。beans。BeansException;import org。springframework。beans。factory。config。InstantiationAwareBeanPostProcessor;import org。springframework。stereotype。Component; @Componentpublic class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor { // 例項化前置 @Override public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { System。out。println(“postProcessBeforeInstantiation被呼叫了——在物件例項化之前呼叫——-beanName:” + beanName); // 預設什麼都不做,返回null return null; } // 例項化後置 @Override public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { System。out。println(“postProcessAfterInstantiation被呼叫了————-beanName:” + beanName); //預設返回true,什麼也不做,繼續下一步 return true; } // 屬性修改 @Override public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException { System。out。println(“postProcessPropertyValues被呼叫了————-beanName:”+beanName); // 此方法可對bean中的屬性值進行、新增、修改、刪除操作; // 對屬性值進行修改,如果postProcessAfterInstantiation方法返回false,該方法可能不會被呼叫, return pvs; }}

下面我們依次解釋

1、例項化前置

例項化前置使用的是 InstantiationAwareBeanPostProcessor。postProcessBeforeInstantiation(Class<?> beanClass, String beanName) 方法,方法裡有2個引數,分別是beanClass和beanName,顧名思義,就是對在物件例項化之前對bean物件的class資訊進行修改或者擴充套件,以達到我們想要的功能,它的底層是動態代理AOP技術實現的;且是bean生命週期中最先執行的方法;

返回非空:返回值是Object型別,這意味著我們可以返回任何型別的值,由於這個時候目標物件還未例項化,所以這個返回值可以用來代替原本該生成物件的目標物件的例項,也就是說,如果返回了非空的值,那麼以後我們需要用到這個bean的時候,拿到的就現在返回的物件了,也就不會去走第二步去例項化物件了;

返回空(null)值:預設也是返回null值的,那麼就直接返回,接下來會呼叫doCreateBean方法來例項化物件;

2、例項化物件

doCreateBean方法建立例項,用反射技術建立,這個沒什麼好說的,只是相當於new了一個物件出來而已,但需要注意的是,這個時候只是將物件例項化了,物件內的屬性還未設定;

3、例項化後置

方法名稱:InstantiationAwareBeanPostProcessor。postProcessAfterInstantiation(Object bean, String beanName)

在目標物件例項化之後呼叫,這個時候物件已經被例項化,但是該例項的屬性還未被設定,都是null。因為他的返回值是決定要不要呼叫postProcessPropertyValues方法中的一個因素(因為還有一個因素是mbd。getDependencyCheck());

返回false :如果該方法返回false,並且不需要check,那麼postProcessPropertyValues就會被忽略不執行;

返回true :如果返回true,postProcessPropertyValues就會被執行

4、屬性修改

方法名稱 :InstantiationAwareBeanPostProcessor。PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)

此方法可對屬性值進行修改,修改範圍包括新增、修改、刪除操作;,如果例項化後置 postProcessAfterInstantiation() 方法返回false,那麼該方法不會被呼叫;

5、給使用者屬性賦值

使用者屬性指的是用spring 的人自定義的bean物件屬性,像 User、Student、Teacher 、UserService、IndexService 這類的物件都是自定義bean物件,第5步主要給這類屬性進行賦值操作,使用的是 AbstractAutowireCapableBeanFactory。populateBean() 方法進行賦值;

6、給容器屬性賦值

容器屬性其實就是容器自帶的屬性,這些屬性都是spring本來就有的;可以肯定的是,它們都是 Aware 介面的實現類,主要有以下實現類,我已經將它們的執行順序都排列好了,

可以硬剛面試官的:Spring IOC詳解 以及 Bean生命週期詳細過程

我們先看看怎麼用,然後再來講解每個Aware的作用;上程式碼

package com。Spring。Boot。init。aware; import org。springframework。beans。BeansException;import org。springframework。beans。factory。BeanClassLoaderAware;import org。springframework。beans。factory。BeanFactory;import org。springframework。beans。factory。BeanFactoryAware;import org。springframework。beans。factory。BeanNameAware;import org。springframework。context。*;import org。springframework。context。annotation。ImportAware;import org。springframework。context。weaving。LoadTimeWeaverAware;import org。springframework。core。env。Environment;import org。springframework。core。io。ResourceLoader;import org。springframework。core。type。AnnotationMetadata;import org。springframework。instrument。classloading。LoadTimeWeaver;import org。springframework。stereotype。Component;import org。springframework。util。StringValueResolver;import org。springframework。web。context。ServletContextAware;import javax。servlet。ServletContext; @Componentpublic class AllAwareInterface implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, EnvironmentAware, EmbeddedValueResolverAware, ResourceLoaderAware, ApplicationEventPublisherAware, MessageSourceAware, ApplicationContextAware, ServletContextAware, LoadTimeWeaverAware, ImportAware { @Override public void setBeanName(String name) { // BeanNameAware作用:讓Bean對Name有知覺 //這個方法只是簡單的返回我們當前的beanName,聽官方的意思是這個介面更多的使用在spring的框架程式碼中,實際開發環境應該不建議使用 System。out。println(“1 我是 BeanNameAware 的 setBeanName 方法 ——-引數:name,內容:”+ name); } @Override public void setBeanClassLoader(ClassLoader classLoader) { System。out。println(“2 我是 BeanClassLoaderAware 的 setBeanClassLoader 方法”); } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { // 注意:如果使用 @Configuration 註解的話,setBeanFactory方法會執行2次, System。out。println(“3 我是 BeanFactoryAware 的 setBeanFactory 方法”); } @Override public void setEnvironment(Environment environment) { System。out。println(“4 我是 EnvironmentAware 的 setEnvironment 方法”); } @Override public void setEmbeddedValueResolver(StringValueResolver stringValueResolver) { System。out。println(“5 我是 EmbeddedValueResolverAware 的 setEmbeddedValueResolver 方法”); } @Override public void setResourceLoader(ResourceLoader resourceLoader) { System。out。println(“6 我是 ResourceLoaderAware 的 setResourceLoader 方法”); } @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { System。out。println(“7 我是 ApplicationEventPublisherAware 的 setApplicationEventPublisher 方法”); } @Override public void setMessageSource(MessageSource messageSource) { System。out。println(“8 我是 MessageSourceAware 的 setMessageSource 方法”); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { System。out。println(“9 我是 ApplicationContextAware 的 setApplicationContext 方法”); } @Override public void setServletContext(ServletContext servletContext) { System。out。println(“10 我是 ServletContextAware 的 setServletContext 方法”); } @Override public void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) { //LoadTimeWeaver 簡稱LTW,LTW是AOP的一種實現方式,此方法是為了獲取Aop織入的物件,使用的織入方式是:類載入期織入, // 一般的aop都是執行期織入,就是在執行的時候才進行織入切面方法,但是LTW是在類載入前就被織入了,也就是class檔案在jvm載入之前進行織入切面方法 // 只有在使用 @EnableLoadTimeWeaving 或者存在 LoadTimeWeaver 實現的 Bean 時才會呼叫,順序也很靠後 System。out。println(“11 我是 LoadTimeWeaverAware 的 setLoadTimeWeaver 方法”); } @Override public void setImportMetadata(AnnotationMetadata annotationMetadata) { //只有被其他配置類 @Import(XX。class) 時才會呼叫,這個呼叫對 XX。class 中的所有 @Bean 來說順序是第 1 的。 System。out。println(“12 我是 ImportAware 的 setImportMetadata 方法”); }}

啟動spring後的控制檯列印的部分結果如下:

可以硬剛面試官的:Spring IOC詳解 以及 Bean生命週期詳細過程

可以看到它們的輸出結果按照順序依次排列打印出來了,這就是它的標準順序了;接下來我們瞭解下它們的具體作用

6.1 BeanNameAware.setBeanName()

這個方法只是簡單的返回我們當前的beanName,聽官方的意思是這個介面更多的使用在spring的框架程式碼中,實際開發環境應該不建議使用

6.2 BeanClassLoaderAware.setBeanClassLoader()

獲取Bean的類裝載器,

6.3 BeanFactoryAware.setBeanFactory()

獲取bean工廠,beanFactory讓你可以不依賴注入方式,隨意的讀取IOC容器裡面的物件,不過beanFactory本身還是要注入的。

需要注意的是,一般情況下我們都用 @Component 註解,如果使用 @Configuration 註解的話,setBeanFactory方法會執行2次;

6.4 EnvironmentAware.setEnvironment()

實現了EnvironmentAware介面重寫setEnvironment方法後,在工程啟動時可以獲得application。properties 、xml、yml 的配置檔案配置的屬性值。

6.5 EmbeddedValueResolverAware.setEmbeddedValueResolver()

通常我們使用@Value註解來獲取properties 和 yml 檔案中的值,每個類中都要使用@Value也很繁瑣,實現EmbeddedValueResolverAware介面後就方便多了。用法也跟@Value一樣,需要用${}包裹住;

@Component public class PropertiesUtil implements EmbeddedValueResolverAware { @Override public void setEmbeddedValueResolver(StringValueResolver stringValueResolver) { System。out。println(stringValueResolver。resolveStringValue(“${logging。file}”)); }}

6.6 ResourceLoaderAware.setResourceLoader()

Spring ResourceLoader為我們提供了一個統一的getResource()方法來透過資源路徑檢索外部資源。從而將資源或檔案(例如文字檔案、XML檔案、屬性檔案或影象檔案)載入到Spring應用程式上下文中的不同實現 ,其實說白了,就是用來載入外部資源的;方法中有個引數:ResourceLoader ,這個引數其實就是ApplicationContext(spring 的上下文物件);可直接強轉;

package org。crazyit。app。service;import org。springframework。context。ResourceLoaderAware;import org。springframework。core。io。ResourceLoader;public class TestBean implements ResourceLoaderAware{ public void setResourceLoader(ResourceLoader resourceLoader) { // 可直接強轉為 ApplicationContext ApplicationContext context = (ApplicationContext) resourceLoader; System。out。println(“6 我是 ResourceLoaderAware 的 setResourceLoader 方法”); } }

並且我們可以指定不同的字首來建立路徑以從不同位置載入資源

可以硬剛面試官的:Spring IOC詳解 以及 Bean生命週期詳細過程

6.7 ApplicationEventPublisherAware.setApplicationEventPublisher();

ApplicationEventPublisherAware是一個事件釋出器的介面,使用這個介面,我們自己的 Service 就擁有了釋出事件的能力。使用者註冊後,不再是顯示呼叫其他的業務 Service,而是釋出一個使用者註冊事件。那麼在這裡是釋出事件,那就肯定有監聽事件的介面,這個介面叫做 ApplicationListener ,只要實現 ApplicationListener 介面就可以接受釋出的事件了,接下來我們寫一個示例來模擬釋出事件和監聽事件;

先建立一個實體類,用來儲存釋出的事件內容 StringEvent。java

package com。Spring。Boot。init。listener。eventModel;import org。springframework。context。ApplicationEvent;//事件監聽物件public class StringEvent extends ApplicationEvent { private String str; // 建構函式 public StringEvent(Object source) { super(source); str = source。toString(); } // 獲取字串 public String getStr(){ return str; }}

建立一個釋出事件的類:

ExtApplicationEventPublisherAware。java

,實現

ApplicationEventPublisherAware

介面增加發布事件的功能;

package com。Spring。Boot。init。aware; import com。Spring。Boot。init。listener。eventModel。StringEvent;import org。springframework。context。ApplicationEventPublisher;import org。springframework。context。ApplicationEventPublisherAware;import org。springframework。stereotype。Component; /** * 釋出事件 */@Componentpublic class ExtApplicationEventPublisherAware implements ApplicationEventPublisherAware { @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { System。out。println(“釋出事件,事件物件為 StringEvent ,內容為 :1234”); StringEvent stringEvent = new StringEvent(“1234”); // 釋出事件 ,釋出後會在 ApplicationListener。onApplicationEvent()方法進行捕獲; applicationEventPublisher。publishEvent(stringEvent); // 釋出事件 }}

在建立一個事件監聽器:

EventListener。java

,用來監聽所有釋出的事件;

package com。Spring。Boot。init。listener; import com。Spring。Boot。init。listener。eventModel。StringEvent;import org。springframework。context。ApplicationEvent;import org。springframework。context。ApplicationListener;import org。springframework。stereotype。Component; //事件監聽器@Componentpublic class EventListener implements ApplicationListener { @Override public void onApplicationEvent(StringEvent o) { System。out。println(“監聽到事件,內容:”+o。getStr()); }}

接下來,執行spring專案,看看列印的結果如下,到這裡,事件的釋出和監聽就完成了;

可以硬剛面試官的:Spring IOC詳解 以及 Bean生命週期詳細過程

6.8 MessageSourceAware.setMessageSource()

國際化訊息通知操作

6.9 ApplicationContextAware.setApplicationContext()

ApplicationContextAware 主要用來全域性獲取 ApplicationContext 上下文,ApplicationContext其實就是容器,為此我們可以實現 ApplicationContextAware 介面來獲取ApplicationContext容器物件;我們可以把它做成一個公共的靜態類,這樣可以在任意地方想拿就拿了,

package com。Spring。Boot。init。aware; import org。springframework。beans。BeansException;import org。springframework。context。ApplicationContext;import org。springframework。context。ApplicationContextAware;import org。springframework。context。annotation。Configuration;import org。springframework。stereotype。Component; @Componentpublic class ExtApplicationContextAware implements ApplicationContextAware { /** * Spring容器會在載入完Spring容器後呼叫ApplicationContextAware。setApplicationContext方法 * ApplicationContextAware 主要用來全域性獲取 ApplicationContext 上下文, */ private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { if (ExtApplicationContextAware。applicationContext == null) { ExtApplicationContextAware。applicationContext = applicationContext; } System。out。println(“========ApplicationContext配置成功========”); System。out。println(“========在普通類可以透過呼叫SpringBootBeanUtil。getApplicationContext()獲取applicationContext物件========”); System。out。println(“========applicationContext=”+ ExtApplicationContextAware。applicationContext +“========”); } /** * 獲取applicationContext * @return */ public static ApplicationContext getApplicationContext() { return applicationContext; } /** * 透過name獲取 Bean。 * @param name * @return */ public static Object getBean(String name) { return getApplicationContext()。getBean(name); } /** * 透過class獲取Bean。 * @param clazz * @return */ public static T getBean(Class clazz) { return getApplicationContext()。getBean(clazz); } /** * 透過name,以及Clazz返回指定的Bean * @param name * @param clazz * @return */ public static T getBean(String name, Class clazz) { return getApplicationContext()。getBean(name, clazz); } }

當然,也可以直接注入,就像這樣:

@Autowired private ApplicationContext applicationContext;

6.10 ServletContextAware.setServletContext()

透過實現ServletContextAware介面可獲取servletContext,也就是servlet的上下文;

什麼是ServletContext :WEB容器在啟動時,它會為每個WEB應用程式都建立一個對應的ServletContext物件,它代表當前web應用。ServletConfig物件中維護了ServletContext物件的引用,開發人員在編寫servlet時,可以透過ServletConfig。getServletContext方法獲得ServletContext物件。

由於一個WEB應用中的所有Servlet共享同一個ServletContext物件,因此Servlet物件之間可以透過ServletContext物件來實現通訊。ServletContext物件通常也被稱之為context域物件。

6.11 LoadTimeWeaverAware.setLoadTimeWeaver()

其實在除錯的時候還有2個沒打印出來,第11個就是 LoadTimeWeaver, 簡稱LTW,LTW是AOP的一種實現方式,此方法是為了獲取Aop織入的物件,使用的織入方式是:類載入期織入,

一般的aop都是執行期織入,就是在執行的時候才進行織入切面方法,但是LTW是在類載入前就被織入了,也就是class檔案在jvm載入之前進行織入切面方法

只有在使用 @EnableLoadTimeWeaving 或者存在 LoadTimeWeaver 實現的 Bean 時才會呼叫,順序也很靠後;

6.12 ImportAware.setImportMetadata()

還有一個沒列印的就是ImportAware介面,這個介面的方法只有被其他配置類 @Import(XX。class) 時才會呼叫,這個呼叫對 XX。class 中的所有 @Bean 來說順序是第 1 的。

7、初始化前置

方法名稱:BeanPostProcessor。postProcessBeforeInitialization()

在每一個 Bean 初始化之前執行的方法(有多少 Bean 呼叫多少次)

注意 :啟用該方法後,標註了@PostConstruct註解的方法會失效

12、初始化後置

方法名稱:BeanPostProcessor。postProcessAfterInitialization()

在每一個 Bean 初始化之後執行的方法(有多少 Bean 呼叫多少次)

初始化前置和初始化後置的實現程式碼如下:

package com。Spring。Boot。init;import org。springframework。beans。BeansException;import org。springframework。beans。factory。config。BeanPostProcessor;import org。springframework。stereotype。Component; @Componentpublic class ExtBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { // 在每一個 Bean 初始化之前執行的方法(有多少 Bean 呼叫多少次) // 注意 :啟用該方法後,標註了@PostConstruct註解的方法會失效 System。out。println(“初始化前置方法”); return null; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 在每一個 Bean 初始化之後執行的方法(有多少 Bean 呼叫多少次) System。out。println(“初始化後置方法”); return null; }}

8、執行初始化方法

初始化方法有三個,分別是 添加了@PostConstruct 註解的方法、實現InitializingBean介面、在@bean註解上新增 initMethod屬性;我們一個個講

9、初始化方法一:@PostConstruct

在bean物件內新增@PostConstruct 註解後即可實現初始化的功能,被@PostConstruct修飾的方法會在建構函式之後,init()方法之前執行。有多個則會執行多次;

注意:如果spring 實現了 BeanPostProcessor介面的postProcessBeforeInitialization() 方法,也就是12的初始後置方法,那麼@PostConstruct註解會失效;

程式碼示例

package com。Spring。Boot。init;import org。springframework。stereotype。Component;import javax。annotation。PostConstruct; // @PostConstruct註解@Componentpublic class ExtPostConstruct { /** * 被@PostConstruct修飾的方法會在建構函式之後,init()方法之前執行。如果有多個則會執行多次 * 注意:如果spring 實現了 BeanPostProcessor介面的postProcessBeforeInitialization方法,該@PostConstruct註解會失效 */ @PostConstruct public void init() { System。out。println(“第一個init。。。”); } // 有多個會執行多次 @PostConstruct public void init1() { System。out。println(“第二個init1。。。”); } }

10、InitializingBean.afterPropertiesSet()

spring 初始化方法之一,作用是在BeanFactory完成屬性設定之後,執行自定義的初始化行為。

執行順序:在initMethod之前執行,在@PostConstruct之後執行

程式碼示例

package com。Spring。Boot。init;import org。springframework。beans。factory。InitializingBean;import org。springframework。context。annotation。Configuration;import org。springframework。stereotype。Component; @Componentpublic class ExtInitializingBean implements InitializingBean { @Override public void afterPropertiesSet() throws Exception { // 一個 InitializingBean 執行一次 // spring 初始化方法,作用是在BeanFactory完成屬性設定之後,執行自定義的 初始化行為。 // 執行順序:在initMethod之前執行,在@PostConstruct之後執行 System。out。println(“InitializingBean”); }}

11、init-method

bean 配置檔案屬性 init-method 用於在bean初始化時指定執行方法,用來替代繼承 InitializingBean介面,

注意的一點是隻有一個類完整的例項被創建出來後,才能走初始化方法。

示例程式碼,先定義一個類:BeanTest。java ,在類中定義一個初始化方法 initMethod_1()

package com。Spring。Boot。init。bean; public class BeanTest { // 將要執行的初始化方法 public void initMethod_1(){ System。out。println(“我是beanTest的init方法”); }}

xml 配置方式

註解配置方式

package com。Spring。Boot。init;import com。Spring。Boot。init。bean。BeanTest;import org。springframework。context。annotation。Bean;import org。springframework。stereotype。Component;@Component()public class InitMethod { // 在@Bean註解上新增initMethod屬性,指向類中的 initMethod_1 執行初始化方法 @Bean(initMethod = “initMethod_1”) public BeanTest getBeanTest(){ return new BeanTest(); }}

13、使用中

到這一步,bean物件就已經完全建立好了,是一個完整物件了,並且正在被其他物件使用了;

14、銷燬流程

在這裡需要先說一下,被spring容器管理的bean預設是單例的,預設在類上面有個 @Scope註解,也就是這樣的

package com。Spring。Boot。init; import org。springframework。beans。factory。config。ConfigurableBeanFactory;import org。springframework。context。annotation。Scope;import org。springframework。stereotype。Component; @Component()@Scope(value = ConfigurableBeanFactory。SCOPE_SINGLETON)// @Scope(value = “singleton”) // 也可以這樣寫public class InitMethod { // methods。。。。 }

如果要設定成多例,只需要把@Scope的屬性值改一下就行,就像這樣,

多例模式也叫原型模式,它底層不是重新建立一個bean物件出來,而是使用深複製技術實現的,就是複製一個物件出來進行使用

@Scope(value = ConfigurableBeanFactory。SCOPE_PROTOTYPE)// @Scope(value = “prototype”) // 也可以這樣寫

為什麼要介紹單例和多例呢?因為啊,銷燬流程的走向就跟你是單例還是多例有關;

如果是單例模式,會先執行 DisposableBean。destroy()方法,然後在執行 destroy-Method 方法;

14.1 DisposableBean.destroy()

單例模式的銷燬方式,示例程式碼

package com。Spring。Boot。init。destroy; import org。springframework。beans。factory。DisposableBean;import org。springframework。stereotype。Component; /** * 銷燬方法 */@Componentpublic class ExtDisposableBean implements DisposableBean { @Override public void destroy() throws Exception { System。out。println(“我被銷燬了”); }}

當結束main方法時,控制檯列印的結果如下

可以硬剛面試官的:Spring IOC詳解 以及 Bean生命週期詳細過程

14。2 destory-method方法

還是拿 第11 個流程的例子來講,只不過這次我們在@Bean註解里加上 destroyMethod屬性,指向銷燬方法 :destroyMethod_1()

package com。Spring。Boot。init; import com。Spring。Boot。init。bean。BeanTest;import org。springframework。context。annotation。Bean;import org。springframework。stereotype。Component; @Component()public class InitMethod { // 在@Bean註解上新增initMethod屬性,指向類中的 initMethod_1 執行初始化方法 // 在@Bean註解上新增destroyMethod屬性,指向類中的 destroyMethod_1 執行銷燬方法 @Bean(initMethod = “initMethod_1”,destroyMethod = “destroyMethod_1”) public BeanTest getBeanTest(){ return new BeanTest(); }}

BeanTest。java

package com。Spring。Boot。init。bean; public class BeanTest { // 將要執行的初始化方法 public void initMethod_1(){ System。out。println(“我是beanTest的init方法”); } // 將要執行的銷燬方法 public void destroyMethod_1(){ System。out。println(“我是beanTest的init方法”); } }

xml的配置方式

15、返回bean給使用者,剩下的生命週期由使用者控制

因為多例模式下,spring無法進行管理,所以將生命週期交給使用者控制,使用者用完bean物件後,java垃圾處理器會自動將無用的物件進行回收操作;

spring ioc 和完整的生命週期到這裡就已經完了,如果你明白並理解了這些流程,那麼在面試過程中絕對是遊刃有餘的,拿個高新offer絕對沒問題。

可以硬剛面試官的:Spring IOC詳解 以及 Bean生命週期詳細過程