一文解讀Spring Bean的生命周期
1. Spring IOC容器
1.1 Spring IOC 容器的設(shè)計(jì)
Spring IOC 容器的設(shè)計(jì)主要是基于BeanFactory和ApplicationContext兩個(gè)接口,其中ApplicationContext是BeanFactory的子接口之一,換句話說BeanFactory是Spring IOC容器所定義的最頂層接口,而ApplicationContext是其高級(jí)接口之一,并且對(duì)于BeanFactory功能做了許多有用的擴(kuò)展,所以在絕大部分的工作場(chǎng)景中,都會(huì)使用ApplicationContext作為Spring IOC 容器,如下圖所示:
首先我們定義一個(gè)User實(shí)體類:
public class User { String username; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } @Override public String toString() { return "User{" + "username='" + username + '\'' + '}'; } }
1.1.1 BeanFactory
BeanFactory的使用注冊(cè)Bean對(duì)象以及獲取Bean對(duì)象代碼如下:
DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory(); RootBeanDefinition rootBeanDefinition=new RootBeanDefinition(User.class); beanFactory.registerBeanDefinition("user",rootBeanDefinition); System.out.println(beanFactory.getBean("user",User.class));
1.1.2 ApplicationContext
ApplicationContext的使用注冊(cè)Bean對(duì)象以及獲取Bean對(duì)象代碼如下:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(User.class); applicationContext.refresh(); System.out.println(applicationContext.getBean("user", User.class));
1.2 Spring Bean的生命周期
Spring 容器可以管理 singleton 作用域 Bean 的生命周期,在此作用域下,Spring 能夠精確地知道該 Bean 何時(shí)被創(chuàng)建,何時(shí)初始化完成,以及何時(shí)被銷毀。
而對(duì)于 prototype 作用域的 Bean,Spring 只負(fù)責(zé)創(chuàng)建,當(dāng)容器創(chuàng)建了 Bean 的實(shí)例后,Bean 的實(shí)例就交給客戶端代碼管理,Spring 容器將不再跟蹤其生命周期。每次客戶端請(qǐng)求 prototype 作用域的 Bean 時(shí),Spring 容器都會(huì)創(chuàng)建一個(gè)新的實(shí)例,并且不會(huì)管那些被配置成 prototype 作用域的 Bean 的生命周期。
生命周期主要是為了了解Spring IOC容器初始化和銷毀Bean的過程,通過對(duì)它的學(xué)習(xí)就可以知道如何在初始化和銷毀的時(shí)候加入自定義的方法,以滿足特定的需求。如下圖:
從上圖可以看到,Spring IoC容器對(duì)Bean 的管理還是比較復(fù)雜的,Spring loC容器在執(zhí)行了初始化和依賴注入后,會(huì)執(zhí)行一定的步驟來完成初始化,通過這些步驟我們就能自定義初始化,而在Spring IoC 容器正常關(guān)閉的時(shí)候,它也會(huì)執(zhí)行一定的步驟來關(guān)閉容器,釋放資源。除需要了解整個(gè)生命周期的步驟外,還要知道這些生命周期的接口是針對(duì)什么而言的,首先介紹生命周期的步驟。
1. 如果 Bean 實(shí)現(xiàn)了接口 BeanNameAware的setBeanName方法,那么它就會(huì)調(diào)用這個(gè)方法。
2. 如果 Bean 實(shí)現(xiàn)了接口 BeanFactoryAware 的 setBeanFactory方法,那么它就會(huì)調(diào)用這個(gè)方法。
3. 如果 Bean實(shí)現(xiàn)了接口 ApplicationContextAware 的 setApplicationContext方法,且
Spring loC容器也必須是一個(gè)ApplicationContext 接口的實(shí)現(xiàn)類,那么才會(huì)調(diào)用這個(gè)方法,否則是不調(diào)用的。
4. 如果 Bean 實(shí)現(xiàn)了接口 BeanPostProcessor 的 postProcessBeforeInitialization方法,那么它就會(huì)調(diào)用這個(gè)方法。
5 .如果 Bean實(shí)現(xiàn)了接口BeanFactoryPostProcessor的afterPropertiesSet方法,那么它就會(huì)調(diào)用這個(gè)方法。
6. 如果 Bean自定義了初始化方法,它就會(huì)調(diào)用已定義的初始化方法。
7. 如果Bean 實(shí)現(xiàn)了接口 BeanPostProcessor 的postProcessAfterInitialization方法,完成了這些調(diào)用,這個(gè)時(shí)候Bean 就完成了初始化,那么 Bean就生存在Spring loC的容器中了,使用者就可以從中獲取 Bean的服務(wù)。
8. 當(dāng)服務(wù)器正常關(guān)閉,或者遇到其他關(guān)閉 Spring loC 容器的事件,它就會(huì)調(diào)用對(duì)應(yīng)的方完成Bean 的銷毀,其步驟如下:
如果Bean實(shí)現(xiàn)了接口 DisposableBean 的 destroy方法,那么就會(huì)調(diào)用它。
如果定義了自定義的銷毀方法,那么就會(huì)調(diào)用它。
1.2.1 BeanDefinition
Spring容器啟動(dòng)的過程中,會(huì)將Bean解析成Spring內(nèi)部的BeanDefinition結(jié)構(gòu)。不管是是通過xml配置文件的<Bean>標(biāo)簽,還是通過注解配置的@Bean,還是@Compontent標(biāo)注的類,還是掃描得到的類,它最終都會(huì)被解析成一個(gè)BeanDefinition對(duì)象,最后我們的Bean工廠就會(huì)根據(jù)這份Bean的定義信息,對(duì)bean進(jìn)行實(shí)例化、初始化等等操作。
你可以把BeanDefinition丟給Bean工廠,然后Bean工廠就會(huì)根據(jù)這個(gè)信息幫你生產(chǎn)一個(gè)Bean實(shí)例,拿去使用。
BeanDefinition里面里面包含了bean定義的各種信息,如:bean對(duì)應(yīng)的class、scope、lazy信息、dependOn信息、autowireCandidate(是否是候選對(duì)象)、primary(是否是主要的候選者)等信息。
RootBeanDefinition類:表示根bean定義信息,通常bean中沒有父bean的就使用這種表示。
ChildBeanDefinition類:表示子bean定義信息,如果需要指定父bean的,可以使用ChildBeanDefinition來定義子bean的配置信息,里面有parentName屬性,用來指定父bean的名稱。
GenericBeanDefinition類:通用的bean定義信息,既可以表示沒有父bean的bean配置信息,也可以表示有父bean的子bean配置信息,這個(gè)類里面也有parentName屬性,用來指定父bean的名稱。
ConfigurationClassBeanDefinition類:表示通過配置類中@Bean方法定義bean信息
可以通過配置類中使用@Bean來標(biāo)注一些方法,通過這些方法來定義bean,這些方法配置的bean信息最后會(huì)轉(zhuǎn)換為ConfigurationClassBeanDefinition類型的對(duì)象。
AnnotatedBeanDefinition接口:表示通過注解的方式定義的bean信息。
BeanDefinitionBuilder:構(gòu)建BeanDefinition的工具類
1.2.2 InstantiationAwareBeanPostProcessor和BeanPostProcessor
InstantiationAwareBeanPostProcessor實(shí)際上繼承了BeanPostProcessor接口。InstantiationAwareBeanPostProcessor作用于實(shí)例化階段的前后,BeanPostProcessor作用于初始化階段的前后。如下圖:
BeanPostProcessor是一個(gè)接口,還有很多子接口,這些接口中提供了很多方法,spring在bean生命周期的不同階段,會(huì)調(diào)用BeanPostProcessor中的一些方法,來對(duì)生命周期進(jìn)行擴(kuò)展,bean生命周期中的所有擴(kuò)展點(diǎn)都是依靠這個(gè)集合中的BeanPostProcessor來實(shí)現(xiàn)的。該接口提供了兩個(gè)函數(shù):postProcessBeforeInitialzation( Object bean, String beanName ) 當(dāng)前正在初始化的bean對(duì)象會(huì)被傳遞進(jìn)來,我們就可以對(duì)這個(gè)bean作任何處理。 這個(gè)函數(shù)會(huì)先于InitialzationBean執(zhí)行,因此稱為前置處理。 所有Aware接口的注入就是在這一步完成的。postProcessAfterInitialzation( Object bean, String beanName ) 當(dāng)前正在初始化的bean對(duì)象會(huì)被傳遞進(jìn)來,我們就可以對(duì)這個(gè)bean作任何處理。 這個(gè)函數(shù)會(huì)在InitialzationBean完成后執(zhí)行,因此稱為后置處理。
Spring Aware是Spring定義的回調(diào)接口。何為回調(diào)?就是客戶程序C調(diào)用服務(wù)程序S中的某個(gè)函數(shù)A,然后S又在某個(gè)時(shí)候反過來調(diào)用C中的某個(gè)函數(shù)B,對(duì)于C來說,這個(gè)B便叫做回調(diào)函數(shù)。
1.2.3 測(cè)試生命周期
我們自定義一個(gè)User實(shí)體類,要求Spring容器使用我們自定義的@MyAutowired注解標(biāo)注的構(gòu)造方法進(jìn)行構(gòu)造Bean對(duì)象,然后我們觀察在Bean周期的日志打印,更好的理解Bean周期過程。
自定義@MyAutowired注解
@Retention(RetentionPolicy.RUNTIME) @Documented @Target(ElementType.CONSTRUCTOR) public @interface MyAutowired { }
定義User實(shí)體類
public class User implements InitializingBean, DisposableBean { String username; //Spring為了降低對(duì)客戶代碼的侵入性,給bean的配置提供了init-method屬性, // 該屬性指定了在這一階段需要執(zhí)行的函數(shù)名。Spring便會(huì)在初始化階段執(zhí)行我們?cè)O(shè)置的函數(shù)。 // init-method本質(zhì)上仍然使用了InitializingBean接口。 public void init(){ System.out.println(this.getClass().getSimpleName()+" 執(zhí)行自定義初始化方法"); } public User() { } @MyAutowired public User(String username) { this.username = username; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } @Override public String toString() { return "User{" + "username='" + username + '\'' + '}'; } @Override public void destroy() throws Exception { System.out.println("調(diào)用DisposableBean接口的destroy方法"); } //afterPropertiesSet()這一階段也可以在bean正式構(gòu)造完成前增加我們自定義的邏輯, // 但它與前置處理不同,由于該函數(shù)并不會(huì)把當(dāng)前bean對(duì)象傳進(jìn)來,因此在這一步?jīng)]辦法處理對(duì)象本身, // 只能增加一些額外的邏輯。 若要使用它,我們需要讓bean實(shí)現(xiàn)該接口,把要增加的邏輯寫在該函數(shù)中。 // 然后Spring會(huì)在前置處理完成后檢測(cè)當(dāng)前bean是否實(shí)現(xiàn)了該接口,并執(zhí)行afterPropertiesSet函數(shù) @Override public void afterPropertiesSet() throws Exception { System.out.println("調(diào)用afterPropertiesSet方法"); } }
測(cè)試代碼
public class MyTest { public static void main(String[] args) { DefaultListableBeanFactory beanFactory=new DefaultListableBeanFactory(); //InstantiationAwareBeanPostProcessor接口在Bean對(duì)象實(shí)例化前的方法 beanFactory.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() { public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { if(beanName.equals("user")) System.out.println(beanName+" Bean對(duì)象在實(shí)例化之前操作**"); return null; } }); //InstantiationAwareBeanPostProcessor接口在Bean對(duì)象實(shí)例化后的方法 beanFactory.addBeanPostProcessor(new InstantiationAwareBeanPostProcessor() { public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { if(beanName.equals("user")) System.out.println(beanName+" Bean對(duì)象在實(shí)例化之后操作**"); return false; } }); //使用自己自定義含有@MyAutowired注解的構(gòu)造方法,實(shí)例化Bean對(duì)象 beanFactory.addBeanPostProcessor(new SmartInstantiationAwareBeanPostProcessor() { public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException { if(beanName.equals("user")) System.out.println("實(shí)例化Bean對(duì)象"); Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors(); List<Constructor<?>> collect = Arrays.stream(declaredConstructors).filter(i -> i.isAnnotationPresent(MyAutowired.class)).collect(Collectors.toList()); Constructor[] constructors = collect.toArray(new Constructor[collect.size()]); return constructors.length>0?constructors:null; } }); //BeanPostProcessor接口在Bean對(duì)象初始化之前的方法調(diào)用 beanFactory.addBeanPostProcessor(new BeanPostProcessor() { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if(beanName.equals("user")) System.out.println(beanName+" Bean對(duì)象在初始化之前操作**"); return null; } }); //BeanPostProcessor接口在Bean對(duì)象初始化之后的方法調(diào)用 beanFactory.addBeanPostProcessor(new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if(beanName.equals("user")) System.out.println(beanName+" Bean對(duì)象在初始化之后操作**"); return null; } }); RootBeanDefinition rootBeanDefinition= (RootBeanDefinition) BeanDefinitionBuilder.rootBeanDefinition(User.class).setInitMethodName("init").getBeanDefinition(); beanFactory.registerBeanDefinition("user",rootBeanDefinition); beanFactory.registerBeanDefinition("username", BeanDefinitionBuilder.genericBeanDefinition(String.class) .addConstructorArgValue("admin").getBeanDefinition()); System.out.println(beanFactory.getBean("user",User.class)); beanFactory.destroySingletons(); } }
測(cè)試截圖
到此這篇關(guān)于一文解讀Spring Bean的生命周期的文章就介紹到這了,更多相關(guān)Spring Bean生命周期內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java web用servlet監(jiān)聽器實(shí)現(xiàn)顯示在線人數(shù)
這篇文章主要為大家詳細(xì)介紹了java web用servlet監(jiān)聽器實(shí)現(xiàn)顯示在線人數(shù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-03-03JavaSE程序邏輯控制實(shí)現(xiàn)詳細(xì)圖文教程
JavaSE是為了開發(fā)桌面應(yīng)用程序和控制臺(tái)應(yīng)用程序而設(shè)計(jì)的,使用JavaSE可以編寫?yīng)毩⑦\(yùn)行的Java應(yīng)用程序,這篇文章主要給大家介紹了關(guān)于JavaSE程序邏輯控制實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2024-04-04詳解如何配置springboot跳轉(zhuǎn)html頁面
這篇文章主要介紹了詳解如何配置springboot跳轉(zhuǎn)html頁面,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09Mybatis中SqlMapper配置的擴(kuò)展與應(yīng)用詳細(xì)介紹(1)
這篇文章主要介紹了Mybatis中SqlMapper配置的擴(kuò)展與應(yīng)用(1)的相關(guān)資料,非常不錯(cuò)具有參考借鑒價(jià)值,需要的朋友可以參考下2016-11-11Springboot Session共享實(shí)現(xiàn)原理及代碼實(shí)例
這篇文章主要介紹了Springboot Session共享實(shí)現(xiàn)原理及代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08java根據(jù)負(fù)載自動(dòng)抓取jstack?dump詳情
這篇文章主要介紹了java根據(jù)負(fù)載自動(dòng)抓取jstack?dump詳情,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09