Spring中Bean的作用域、實例化方式、生命周期、循環依賴問題
- 一、Bean的作用域
- 1.singleton
- 2.prototype
- 3.其他scope值
- 二、Bean的實例化方式
- 1.通過構造方法實例化
- 2.通過簡單工廠模式實例化
- 3.通過factory-bean實例化
- 4.通過FactoryBean接口實例化
- 5.BeanFactory和FactoryBean的區別
- (1)BeanFactory
- (2)FactoryBean
- 三、Bean的生命周期
- 1.什么是Bean的生命周期
- 2.為什么要知道Bean的生命周期
- 3.Bean的生命周期之5步
- 4.Bean生命周期之7步
- 5.Bean生命周期之10步
- 6.Bean的作用域不同,管理方式不同
- 7.自己new的對象如何讓Spring管理
- 四、Bean的循環依賴問題
- 1.什么是Bean的循環依賴
- 2.singleton下的set注入產生的循環依賴
- 3. prototype下的set注入產生的循環依賴
- 4.singleton下的構造注入產生的循環依賴
- 5.Spring解決循環依賴的機理
一、Bean的作用域
1.singleton
- 默認情況下,Spring的IoC容器創建的Bean對象是單例的。
- 默認情況下,Bean對象的創建是在初始化Spring上下文的時候就完成了。
2.prototype
- 如果想讓Spring的Bean對象以多例的形式存在,可以在bean標簽中指定scope屬性的值為:prototype,這樣Spring會在每一次執行getBean()方法的時候創建Bean對象,調用幾次則創建幾次。
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="sb" class="com.gdb.spring6.beans.SpringBean" scope="prototype" />
</beans>
3.其他scope值
- scope屬性的值不止兩個,它一共包括8個選項:
- singleton:默認的,單例。
- prototype:原型。每調用一次getBean()方法則獲取一個新的Bean對象。或每次注入的時候都是新對象。
- request:一個請求對應一個Bean。僅限于在WEB應用中使用。
- session:一個會話對應一個Bean。僅限于在WEB應用中使用。
- global session:portlet應用中專用的。如果在Servlet的WEB應用中使用global session的話,和session一個效果。(portlet和servlet都是規范。servlet運行在servlet容器中,例如Tomcat。portlet運行在portlet容器中。)
- application:一個應用對應一個Bean。僅限于在WEB應用中使用。
- websocket:一個websocket生命周期對應一個Bean。僅限于在WEB應用中使用。
- 自定義scope:很少使用。
二、Bean的實例化方式
- Spring為Bean提供了多種實例化方式,通常包括4種方式。(也就是說在Spring中為Bean對象的創建準備了多種方案,目的是:更加靈活)
1.通過構造方法實例化
- 參考我的博客====>Spring對IoC的是實現中的Spring的第一個程序。
2.通過簡單工廠模式實例化
- 第一步:定義一個Bean
package com.gdb.spring6.bean;public class Vip {
}
- 第二步:編寫簡單工廠模式當中的工廠類
package com.gdb.spring6.bean;public class VipFactory {public static Vip get(){return new Vip();}
}
- 第三步:在Spring配置文件中指定創建該Bean的方法(使用factory-method屬性指定)
<bean id="vipBean" class="com.gdb.spring6.bean.VipFactory" factory-method="get"/>
3.通過factory-bean實例化
- 這種方式本質上是:通過工廠方法模式進行實例化。
- 第一步:定義一個Bean
package com.gdb.spring6.bean;public class Vip {
}
- 第二步:定義具體工廠類,工廠類中定義實例方法
- 在這里可以在創建Bean的前后進行加工處理。
package com.gdb.spring6.bean;public class VipFactory {public Vip get(){return new Vip();}
}
- 第三步:在Spring配置文件中指定factory-bean以及factory-method
<bean id="vipFactory" class="com.gdb.spring6.bean.VipFactory"/>
<bean id="vipBean" factory-bean="vipFactory" factory-method="get"/>
4.通過FactoryBean接口實例化
- 以上的第三種方式中,factory-bean是我們自定義的,factory-method也是我們自己定義的。
- 在Spring中,當你編寫的類直接實現FactoryBean接口之后,factory-bean不需要指定了,factory-method也不需要指定了。
- factory-bean會自動指向實現FactoryBean接口的類,factory-method會自動指向getObject()方法。
- 第一步:定義一個Bean
package com.gdb.spring6.bean;public class Vip {
}
- 第二步:編寫一個類實現FactoryBean接口
package com.gdb.spring6.bean;import org.springframework.beans.factory.FactoryBean;public class VipFactoryBean implements FactoryBean<Vip> {@Overridepublic Vip getObject() throws Exception {return new Vip ();}@Overridepublic Class<?> getObjectType() {return null;}@Overridepublic boolean isSingleton() {// true表示單例// false表示原型return true;}
}
- 第三步:在Spring配置文件中配置FactoryBean
<bean id="vipBean" class="com.gdb.spring6.bean.VipFactoryBean"/>
- FactoryBean在Spring中是一個接口。被稱為“工廠Bean”。“工廠Bean”是一種特殊的Bean。所有的“工廠Bean”都是用來協助Spring框架來創建其他Bean對象的。
- 其實FactoryBean就是一個抽象工廠。
- 通過FactoryBean這個工廠Bean主要是想對普通Bean進行加工處理。
5.BeanFactory和FactoryBean的區別
(1)BeanFactory
- Spring IoC容器的頂級對象,BeanFactory被翻譯為“Bean工廠”,在Spring的IoC容器中,“Bean工廠”負責創建Bean對象。
- BeanFactory是工廠。
(2)FactoryBean
- FactoryBean:它是一個Bean,是一個能夠
輔助Spring
實例化其它Bean對象的一個Bean。 - 在Spring中,Bean可以分為兩類:
- 第一類:普通Bean
- 第二類:工廠Bean(記住:工廠Bean也是一種Bean,只不過這種Bean比較特殊,它可以輔助Spring實例化其它Bean對象。)
三、Bean的生命周期
1.什么是Bean的生命周期
- Spring其實就是一個管理Bean對象的工廠。它負責對象的創建,對象的銷毀等。
- 所謂的生命周期就是:對象從創建開始到最終銷毀的整個過程。
- 什么時候創建Bean對象?
- 創建Bean對象的前后會調用什么方法?
- Bean對象什么時候銷毀?
- Bean對象的銷毀前后調用什么方法?
2.為什么要知道Bean的生命周期
- 其實生命周期的本質是:在哪個時間節點上調用了哪個類的哪個方法。
- 我們需要充分的了解在這個生命線上,都有哪些特殊的時間節點。
- 只有我們知道了特殊的時間節點都在哪,到時我們才可以確定代碼寫到哪。
- 我們可能需要在某個特殊的時間點上執行一段特定的代碼,這段代碼就可以放到這個節點上。當生命線走到這里的時候,自然會被調用。
3.Bean的生命周期之5步
- Bean生命周期的管理,可以參考Spring的源碼:AbstractAutowireCapableBeanFactory類的doCreateBean()方法。
- Bean生命周期可以粗略的劃分為五大步:
- 第一步:實例化Bean
- 第二步:Bean屬性賦值
- 第三步:初始化Bean
- 第四步:使用Bean
- 第五步:銷毀Bean
- 需要注意的:
- 第一:只有正常關閉spring容器,bean的銷毀方法才會被調用。
- 第二:ClassPathXmlApplicationContext類才有close()方法。
- 第三:配置文件中的init-method指定初始化方法。destroy-method指定銷毀方法。(初始化和銷毀方法,需要自己在Bean中編寫,然后在配置文件中進行配置)
4.Bean生命周期之7步
- 在以上的5步中,第3步是初始化Bean,如果你還想在初始化前和初始化后添加代碼,可以加入“Bean后處理器”。
- 編寫一個類實現BeanPostProcessor類,并且重寫before和after方法:
package com.gdb.spring6.bean;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;public class LogBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("Bean后處理器的before方法執行,即將開始初始化");return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("Bean后處理器的after方法執行,已完成初始化");return bean;}
}
- 在spring.xml文件中配置“Bean后處理器”:
<!--配置Bean后處理器。這個后處理器將作用于當前配置文件中所有的bean。-->
<bean class="com.powernode.spring6.bean.LogBeanPostProcessor"/>
- 一定要注意:
在spring.xml文件中配置的Bean后處理器將作用于當前配置文件中所有的Bean。
5.Bean生命周期之10步
6.Bean的作用域不同,管理方式不同
- Spring 根據Bean的作用域來選擇管理方式。
- 對于singleton作用域的Bean,Spring 能夠精確地知道該Bean何時被創建,何時初始化完成,以及何時被銷毀;
- 而對于 prototype 作用域的 Bean,Spring 只負責創建,當容器創建了 Bean 的實例后,Bean 的實例就交給客戶端代碼管理,Spring 容器將不再跟蹤其生命周期。
*對于 prototype 作用域Spring容器管理 Bean生命周期的前八步。
7.自己new的對象如何讓Spring管理
- 有些時候可能會遇到這樣的需求,某個java對象是我們自己new的,然后我們希望這個對象被Spring容器管理,怎么實現?
package com.gdb.spring6.test;import com.gdb.spring6.bean.User;
import org.junit.Test;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;public class RegisterBeanTest {@Testpublic void testBeanRegister(){// 自己new的對象User user = new User();System.out.println(user);// 創建 默認可列表BeanFactory 對象DefaultListableBeanFactory factory = new DefaultListableBeanFactory();// 注冊Beanfactory.registerSingleton("userBean", user);// 從spring容器中獲取beanUser userBean = factory.getBean("userBean", User.class);System.out.println(userBean);}
}
四、Bean的循環依賴問題
1.什么是Bean的循環依賴
- A對象中有B屬性。B對象中有A屬性。這就是循環依賴。我依賴你,你也依賴我。
2.singleton下的set注入產生的循環依賴
- 在singleton + setter模式注入的情況下,循環依賴是沒有問題的。Spring可以解決這個問題。
- 主要原因是:在這種模式下 Spring 對 Bean 的管理主要分為清晰的兩個階段:
- 第一階段:在Spring容器加載的時候,實例化Bean,只要其中任意一個Bean實例化之后,馬上進行“曝光”【不等屬性賦值曝光】。
- 第二階段:Bean“曝光”之后,再進行屬性的賦值(調用set方法。)
- 核心解決方案是:
實例化對象和對象的屬性賦值分為兩個階段來完成的。
- 主要原因是:在這種模式下 Spring 對 Bean 的管理主要分為清晰的兩個階段:
3. prototype下的set注入產生的循環依賴
- 當循環依賴的所有Bean的scope="prototype"的時候,產生的循環依賴,Spring是無法解決的,會出現
BeanCurrentlyInCreationException
異常。 - 如果其中一個是singleton,另一個是prototype,是沒有問題的。
4.singleton下的構造注入產生的循環依賴
- 因為構造方法注入會導致實例化對象的過程和對象屬性賦值的過程沒有分離開,必須在一起完成導致的。
5.Spring解決循環依賴的機理
- 總結:
Spring只能解決setter方法注入的單例bean之間的循環依賴。ClassA依賴ClassB,ClassB又依賴ClassA,形成依賴閉環。Spring在創建ClassA對象后,不需要等給屬性賦值,直接將其曝光到bean緩存當中。在解析ClassA的屬性時,又發現依賴于ClassB,再次去獲取ClassB,當解析ClassB的屬性時,又發現需要ClassA的屬性,但此時的ClassA已經被提前曝光加入了正在創建的bean的緩存中,則無需創建新的的ClassA的實例,直接從緩存中獲取即可。從而解決循環依賴問題。