目錄
一.Bean生命周期的簡介
1.基本概念
2.Spring生命周期的幾大階段
3.注意點及小結
?4.生活案例
5.Spring容器管理JavaBean的初始化過程
二. Bean的單例選擇與多例選擇
1.單例選擇與多例選擇的優缺點
1.1單例模式的優點:
1.2單例模式的缺點:
1.3多例模式的優點:
1.4多例模式的缺點:
1.5 小結
2.案例演示單例模式與多例模式
1.演示在單例模式和多例模式下資源變量是否被污染
2. 判斷單例模式和多例模式的初始化
3.單例模式和多例模式的適用場景
一.Bean生命周期的簡介
1.基本概念
Bean生命周期指的是Bean在容器中創建、初始化、使用和銷毀的過程。Spring的IoC容器負責管理Bean的生命周期,確保它們按照預期的方式被創建和銷毀
2.Spring生命周期的幾大階段
實例化:在容器啟動時,Spring根據配置文件或注解等方式創建Bean的實例。這個階段是通過調用Bean的構造函數完成的。
屬性注入:在實例化后,Spring會通過各種方式(例如XML配置、注解、Java代碼等)將Bean所需的屬性值注入到Bean中。可以使用
@Autowired
和@Value
等注解來實現依賴注入。初始化回調方法:在Bean的所有屬性被注入之后,Spring會調用Bean的初始化回調方法。常見的初始化回調方法是實現
InitializingBean
接口的afterPropertiesSet
方法或使用@PostConstruct
注解。自定義初始化方法:除了使用初始化回調接口和注解外,開發人員還可以在Bean中定義自己的初始化方法。可以通過在Bean的方法上使用
@Bean(initMethod = "customInit")
注解來指定自定義的初始化方法。使用Bean:在初始化后,Bean可以被容器和其他Bean使用。可以在其他Bean中使用依賴注入來獲取到已經初始化的Bean。
銷毀回調方法:當容器關閉時或者通過編程方式銷毀Bean時,Spring會調用Bean的銷毀回調方法。常見的銷毀回調方法是實現
DisposableBean
接口的destroy
方法或使用@PreDestroy
注解。自定義銷毀方法:類似于初始化方法,開發人員可以在Bean中定義自己的銷毀方法。可以通過在Bean的方法上使用
@Bean(destroyMethod = "customDestroy")
注解來指定自定義的銷毀方法。
3.注意點及小結
當使用Java配置(如
@Configuration
)時,Bean的生命周期管理通常使用方法級別的@Bean
注解,而不是XML配置中的<bean>
元素。總結起來,Spring Bean的生命周期包括實例化、屬性注入、初始化回調方法、自定義初始化方法、使用Bean、銷毀回調方法和自定義銷毀方法。Spring提供了多種方式來管理Bean的生命周期,開發人員可以根據需要選擇適合自己的方式
?4.生活案例
假設我們以一個人的生命周期作為生活實例來比喻Spring Bean的生命周期。
實例化:當一個人出生時,就相當于Bean在容器中被實例化的過程。一個新生嬰兒就是一個新的實例,具有獨立的身份。
屬性注入:隨著時間的推移,這個人會經歷各種學習和成長的過程,就像Bean在屬性注入階段接收到不同的屬性值。例如,這個人會接受教育、學習技能、掌握知識,這些都是人生中注入的屬性。
初始化回調方法:當這個人成年后,可能會選擇一個職業或自己的事業,就像Bean在初始化回調方法階段定義自己的職責和目標。這個人會明確自己的使命,并準備好開始工作。
自定義初始化方法:在人生的旅程中,這個人會發展自己的興趣愛好、價值觀和生活方式,就像Bean可以定義自己的初始化方法。這些方法可以幫助這個人構建自己的身份,并適應不同的環境。
使用:完成初始化后,這個人會活躍在社會中,與其他人交流、工作、合作,就像Bean在容器中被其他組件或應用程序使用。
銷毀回調方法:當一個人的生命接近尾聲或者出現一些變故時,他們開始為離開做準備。就像Bean的銷毀回調方法一樣,這個人可能會處理他們的后事,與親人和朋友告別,做好最后的安排。
自定義銷毀方法:在離開之前,這個人可以進行一些必要的準備,如寫遺囑、處理財務等,就像Bean可以定義自己的銷毀方法來處理必要的清理工作。
通過這個比喻,我們可以將Spring Bean的生命周期與一個人的生命周期進行對比,從而更好地理解Bean在容器中的創建、初始化、使用和銷毀的過程。
?
?Bean的生命周期就像我們的一生,人的終點是死亡,Bean的生命周期也一樣,終點都是一樣的,重要的是過程,所以這里也希望大家能夠熱愛生活
5.Spring容器管理JavaBean的初始化過程
1.xml/annotation/configuation 配置Javabean
2.BeanDefinitionReader解析配置的JavaBean得到BeanDefinition,最終得到List<BeanDefinition>集合
3.觸發BeanFactoryPostProcessor,在Javabean初始化之前執行自己的業務
4.spring中beanFactory,會通過List<BeanDefinition>集合遍歷初始化所有的Javabean對象
5.如果自己的JavaBean需要調動Spring上下文中的資源,那么需要實現*aware感知接口
6.如果自己的JavaBean已經初始化好了,還需擴展功能,那么需要借助BeanPostProcessor來實現
二. Bean的單例選擇與多例選擇
默認狀態下Bean的配置的單例的,當然,也可以選擇去配置多例的
1.單例選擇與多例選擇的優缺點
1.1單例模式的優點:
- 資源利用:單例模式在應用程序啟動時創建一個實例,并一直重用該實例,減少了對象創建和銷毀的開銷,可以有效利用系統資源。
- 全局性:單例模式可以在整個應用程序中共享數據,確保數據的一致性和可靠性。
- 狀態共享:由于單例只有一個實例,可以方便地在不同的組件之間共享狀態,簡化了組件之間的通信和數據傳遞。
- 線程安全:單例模式天生具有線程安全的特性,因為只有一個實例在操作,避免了并發訪問的問題。
1.2單例模式的缺點:
- 資源持有:單例模式的實例會一直存在于內存中,占用一定的系統資源,在某些情況下可能會導致資源浪費。
- 生命周期管理:單例模式的實例生命周期長,無法自動釋放資源,需要手動管理對象狀態和清理資源。
- 難以模擬測試:由于單例模式的全局性和狀態共享的特性,對于單元測試和模擬測試來說可能會更加困難,因為需要考慮到全局狀態的影響。
1.3多例模式的優點:
- 靈活性:多例模式可以根據需要創建多個實例,每個實例之間相互獨立并具有自己的狀態,可以更靈活地應對不同的需求。
- 隔離性:多例模式的實例相互獨立,不共享狀態,可以避免狀態污染和互相干擾的問題。
- 生命周期管理:每個多例實例的生命周期由Spring容器管理,可以自動釋放資源,減少內存泄漏的風險。
- 更易于測試:多例模式的實例相互獨立,可以更容易地進行單元測試和模擬測試,測試之間不會相互影響。
1.4多例模式的缺點:
- 資源占用:多例模式創建了多個實例,會占用更多的內存和系統資源。
- 對象管理復雜:多個實例需要由開發者自己管理和控制,需要注意實例的創建、銷毀和狀態管理,增加了設計和開發的復雜性。
- 線程安全:多例模式的實例在并發環境下需要額外考慮線程安全的問題,確保實例之間的數據不會沖突。
1.5 小結
單例模式適用于需要全局共享和狀態共享的情況,能夠提高資源利用和數據一致性,但需要注意資源管理和生命周期的維護。而多例模式適用于需要靈活性和隔離性的情況,能夠減少資源占用和相互影響,但需要開發者自行管理和控制實例的創建和狀態。選擇單例還是多例取決于具體的業務需求和系統設計的考慮。
2.案例演示單例模式與多例模式
1.演示在單例模式和多例模式下資源變量是否被污染
1.準備好資源
package com.YU.beanlife;import java.util.List;import com.YU.ioc.service.UserService;
import com.YU.ioc.service.impl.UserServiceImpl1;
import com.YU.ioc.service.impl.UserServiceImpl2;public class ParamAction {private int age;private String name;private List<String> hobby;private int num = 1;// private UserBiz userBiz = new UserBizImpl1();public ParamAction() {super();}public ParamAction(int age, String name, List<String> hobby) {super();this.age = age;this.name = name;this.hobby = hobby;}public void execute() {// userBiz.upload();// userBiz = new UserBizImpl2();System.out.println("this.num=" + this.num++);System.out.println(this.name);System.out.println(this.age);System.out.println(this.hobby);}
}
2.準備好Bean工廠
package com.YU.beanlife;public class InstanceFactory {public void init() {System.out.println("初始化方法");}public void destroy() {System.out.println("銷毀方法");}public void service() {System.out.println("業務方法");}
}
3.配置xml中的bean目錄
<!--spring的生命周期--><bean class="com.YU.beanlife.ParamAction" id="paramAction"><constructor-arg name="name" value="死仔"></constructor-arg><constructor-arg name="age" value="21"></constructor-arg><constructor-arg name="hobby"><list><value>抽煙</value><value>打go</value><value>燙頭</value></list></constructor-arg></bean><bean id="instanceFactory" class="com.YU.beanlife.InstanceFactory"scope="prototype" init-method="init" destroy-method="destroy"></bean>
4.編寫測試程序
package com.YU.beanlife;import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;/** spring bean的生命週期* spring bean的單例多例*/
public class Demo2 {// 體現單例與多例的區別@Testpublic void test1() {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml");
// ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml");ParamAction p1 = (ParamAction) applicationContext.getBean("paramAction");ParamAction p2 = (ParamAction) applicationContext.getBean("paramAction");// System.out.println(p1==p2);p1.execute();p2.execute();// 單例時,容器銷毀instanceFactory對象也銷毀;多例時,容器銷毀對象不一定銷毀;applicationContext.close();}// 體現單例與多例的初始化的時間點 instanceFactory@Testpublic void test2() {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/spring-context.xml");InstanceFactory instanceFactory = (InstanceFactory) applicationContext.getBean("instanceFactory");}// BeanFactory會初始化bean對象,但會根據不同的實現子類采取不同的初始化方式// 默認情況下bean的初始化,單例模式立馬會執行,但是此時XmlBeanFactory作為子類,單例模式下容器創建,bean依賴沒有初始化,只有要獲取使用bean對象才進行初始化@Testpublic void test3() {// ClassPathXmlApplicationContext applicationContext = new// ClassPathXmlApplicationContext("/spring-context.xml");Resource resource = new ClassPathResource("/spring-context.xml");BeanFactory beanFactory = new XmlBeanFactory(resource);
// InstanceFactory i1 = (InstanceFactory) beanFactory.getBean("instanceFactory");}}
其中Test1測試結果為:
由于我們默認使用的是單例模式,所以在運行時出現了變量污染,num值變為2?
當我們配置多例模式同樣運行Test1時
scope="prototype"
?運行結果:
由此可得知我們的變量沒有被污染?
2. 判斷單例模式和多例模式的初始化
配置和上面是一樣的
1.當我們運行Test2時測試JavaBean是否跟著初始化
單例模式運行結果:
由單例模式可以看出發生了初始化?
多例模式運行結果:
由此得知并沒有出現初始化
當我們用多例模式運行Test3時
運行結果:
?看到紅框中的代碼,當我們的JavaBean時才會初始化
小結:
由三次運行結果我們可以得出:
1.單例模式中的JavaBean是跟著Spring上下文初始化的,容器生成對象跟著生成,容器死亡,對象死亡
2.多例模式走的Javabean是使用時才會創建,銷毀要跟著Jvm走
3.單例模式和多例模式的適用場景
單例模式和多例模式在不同的場景下具有不同的適用性。以下是它們常見的適用場景:
適用于單例模式的場景:
- 資源共享:當需要在應用程序的多個組件之間共享同一份資源或數據時,單例模式可以確保全局范圍內的數據一致性。
- 工廠類:當需要創建一個全局工廠類來統一管理對象的創建和生命周期時,單例模式可以確保該工廠類始終只有一個實例。
- 配置信息:當需要在應用程序中加載一份全局的配置信息,并且多個組件需要共享該配置信息時,單例模式可以確保配置信息的一致性和高效訪問。
- 日志記錄器:當需要在整個應用程序中使用同一個日志記錄器來記錄日志時,單例模式可以確保日志的一致性和集中管理。
適用于多例模式的場景:
- 并發請求處理:當需要在多線程或并發環境下處理請求,并且每個請求使用獨立的實例來保證狀態隔離時,多例模式可以為每個請求創建一個獨立的對象。
- 對象池:當需要管理一組可復用的對象,并且對象在不同的時刻需要創建和銷毀時,多例模式可以提供對象池來管理對象的生命周期,以減少創建和銷毀的開銷。
- 狀態管理:當對象的狀態需要在不同的上下文環境中獨立維護和處理時,多例模式可以為每個上下文環境創建一個獨立的實例,以避免狀態沖突和相互干擾。
- 服務提供者:當系統需要支持多個相同類型的服務提供者,并且每個服務提供者需要獨立的實例時,多例模式可以滿足服務提供者的創建和管理需求。
注意!!!
單例模式在一些場景下可能存在共享資源競爭、線程安全等問題,需要謹慎設計和考慮并發訪問的情況。多例模式在一些場景下可能會增加資源消耗和對象管理的復雜性,需要權衡資源利用和代碼復雜度之間的平衡。在實際應用中,根據具體的業務需求和系統設計要求來選擇單例模式或多例模式。
?
?很感謝各位大佬的觀看,點個關注不迷路,大家的點贊和收藏是博主最大的創作動力,謝謝啦