1. Bean的作用域
Bean 的作用域指的是 Bean 在 Spring 容器中的行為(Bean 實例創(chuàng)建及生命周期),它的行為是由 Spring 來管理的,可以根據(jù)具體情況選擇不同的作用域來達到性能優(yōu)化、資源利用最大化和可維護性等目的。
Bean 作用域(Scope)類型主要有如下幾種:
其中前兩種是 Spring 核心作用域,而后 4 種是 Spring MVC 中的作?域;
- singleton:單例模式(默認(rèn)的作用域),Spring IoC 容器中只會存在一個共享的 Bean 實例,無論有多少個 Bean 引用它,始終指向同一對象,通常無狀態(tài)的 Bean 使用該模式(無狀態(tài)表示Bean對象的屬性不需要更新),這也是 Spring 出于性能方面的考慮。
- prototype:原型模式(多例作用域),每次從 Spring 容器中獲取 prototype 模式的 Bean 時,容器都將創(chuàng)建一個新的 Bean 實例,每個Bean實例都有自己的屬性和狀態(tài),而 singleton 全局只有一個對象,通常有狀態(tài)的 Bean 使用該作用域,即Bean的屬性可能會更新。
- request:請求模式,在一次 HTTP 請求中,容器會返回該 Bean 的同一個實例,在這次的請求和響應(yīng)中共享這個 Bean,但在不同的請求中會創(chuàng)建新的實例,也就是說每次 HTTP 請求,使用的是不同的Bean, 類似于 prototype,在 Spring MVC 中使用,即使用需要在 WebApplicationContext 環(huán)境下。
- session:會話模式,在同一個 HTTP Session 中,共享使用的是一個 Bean 實例,例如記錄用戶的登錄信息,在 Spring MVC 中使用。
- application:全局模式,表示整個 Web 應(yīng)用的 http servlet context 共享同一個 Bean,主要記錄的是的 Web 應(yīng)用的上下文信息,例如記錄一個應(yīng)用的共享信息,在 Spring MVC 中使用。
- websocket:網(wǎng)絡(luò)長連接,只適用于 Spring WebSocket 項目,在一個 HTTP WebSocket 的生命周期中(一次長連接),共享一個 Bean 實例;WebSocket 的每次會話中,保存了一個 Map 結(jié)構(gòu)的頭信息,用來保存客戶端消息頭,第一次初始化后,直到一次長連接結(jié)束都是用一個 Bean。
這里就簡單用singleton
和prototype
這兩種可以在 Spring 核心代碼中使用的模式來演示區(qū)別一下。
package com.tr.demo.model;
public class User {
private int id;
private String name;
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
一般有兩種方式設(shè)置 Spring Bean 作用域:
- 使用
xml
配置的方式,我們將bean
標(biāo)簽當(dāng)中的scope
屬性設(shè)置為singleton
就為單例,設(shè)置為prototype
就為多例模式。 - 二是直接通過
@Scope
注解,不設(shè)置參數(shù)或設(shè)置參數(shù)為singleton
就為單例,設(shè)置為prototype
就為多例模式。
我們還可在 Scope 注解中可以傳入另一種參數(shù)形式,和上面直接拼寫出來效果一樣。
這種方式是 ConfigurableBeanFactory 類中為我們提供了靜態(tài)屬性,相較于自己拼寫不容易出錯。
我們來看一下 singleton 和 prototype 兩種模式下獲取的 Bean 有什么不同,我們這里演示在這兩種不同的模式下將 User 對象放到 Spring 容器中,從容器中先后兩次取出 User,看這兩次取出的 Bean 是否相同。
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
User user1 = context.getBean("user", User.class);
User user2 = context.getBean("user",User.class);
System.out.println(user1 == user2);
}
singleton 模式下的結(jié)果:
可以看到,此時 User 類在 Ioc 容器中只創(chuàng)建了一個實例,而通過 applicationContext.getBean() 方法多次獲取到的 Bean,都是同一個對象實例,相當(dāng)于是一個淺拷貝。
prototype 模式下的結(jié)果:
這里之所以這里打印出來的 Bean 是 2 個而不是 3 個,是因為prototype模式下在我們創(chuàng)建容器時并不會進行 Bean 的實例化,它在我們獲取 Bean 的時候才會去創(chuàng)建 1 個實例,而且每次獲取 Bean 時都會創(chuàng)建新的實例,它們彼此之間都是不同的實例,相當(dāng)于是一個深拷貝。
2. Spring的生命周期
Spring 的生命周期大致走的的是如下流程
- 首先啟動容器,加載配置文件(類加載路徑下的 spring-config.xml 文件)。
- 根據(jù)配置文件完成 Bean 的實例化(分配內(nèi)存空間)和初始化(初始化空間),包括掃描配置文件下帶有五大類或者方法注解的類和 bean 標(biāo)簽中要注冊的對象,配置文件中的 Bean 是按順序?qū)嵗统跏蓟摹?br>
- 將 Bean 注冊到 Spring 容器中
- 裝配 Bean 的屬性,如果 Bean 對象需要其他的 Bean 作為屬性,可以使用注解的方式注入(@Autowired、Resource)
小結(jié),Bean執(zhí)行流程(Spring執(zhí)行流程):啟動 Spring 容器 -> 實例化 Bean(分配內(nèi)存空間,從無到有)-> Bean 注冊到 Spring 容器中(存操作) -> 將 Bean 裝配到需要的類中(取操作)。
3. Bean的生命周期
Bean 的生命周期,就是 Bean 對象從誕生到銷毀的整個過程,Bean 的生命周期大致有 5 個階段:
①Bean 的實例化(分配內(nèi)存空間)。
②設(shè)置 Bean 屬性(進行依賴注入,將依賴的 Bean 輔助到當(dāng)前類的屬性上)。
③Bean 的初始化階段:
- 執(zhí)行各種通知,底層實現(xiàn)了 Aware 接口,執(zhí)行其中的方法,如:BeanNameAware、BeanFactoryAware、ApplicationContextAware 的接口方法;
- 執(zhí)行初始化的前置方法,即執(zhí)行 BeanPostProcessor 方法(before);
- 執(zhí)行初始化方法 ,依賴注?操作之后被執(zhí)?(兩種重寫方式:xml方式,注解方式@PostConstruct);
- 執(zhí)???指定的 init-method ?法(如果有指定的話);
- 執(zhí)行 BeanPostProcessor 初始化后置方法(after)。
④使用 Bean。
⑤銷毀 Bean,執(zhí)行 銷毀容器的各種?法,如 @PreDestroy、DisposableBean 接??法、destroy-method。
要注意設(shè)置屬性必須在初始化之前,因為有可能在初始化的時候使用Bean。
上述流程可以以?活中的場景來理解它,?如我們現(xiàn)在需要買?棟房?,那么我們的流程是這樣的:
- 先買房(毛坯房),對應(yīng) Bean 的實例化;
- 裝修,對應(yīng)給 Bean 設(shè)置屬性;
- 買家電,如洗?機、冰箱、電視、空調(diào)等,對應(yīng) Bean 的各種初始化
- ?住,對應(yīng)使? Bean;
- 賣房,對應(yīng) Bean 的銷毀。
執(zhí)行流程如下圖所示:
還需要注意區(qū)分一下實例化和初始化,實例化和屬性設(shè)置是 Java 級別的系統(tǒng)“事件”,其操作過程不可???預(yù)和修改;?初始化是給開發(fā)者 提供的,可以在實例化之后,類加載完成之前進??定義“事件”處理。
下面就簡單的寫幾個周期方法來演示一下這個過程,在類中添加兩個方法,一個是init
方法表示初始化,另外一個是destroy
方法,表示Bean銷毀前執(zhí)行的事情,使用@PostConstruct
注解或者xml
的bean
標(biāo)簽中的init-method
屬性表示在構(gòu)造后執(zhí)行,@PreDestroy
注解或者xml
的bean
標(biāo)簽中的destroy-method
表示在 Bean 銷毀前做的事情。
import com.tr.demo.model.User;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class BeanLifeComponent implements BeanNameAware {
@Autowired
private User user;
@Override
public void setBeanName(String s) {
System.out.println("執(zhí)行了 BeanNameAware 通知 -> " + s);
}
@PostConstruct
public void doPostConstruct() {
System.out.println("執(zhí)行了 @PostConstruct (注解式初始化)");
System.out.println(user.toString());
}
public void myInit(){
System.out.println("執(zhí)行了 myinit (xml 式初始化)");
}
@PreDestroy
public void doPreDestroy(){
System.out.println("執(zhí)行了 @PreDestroy (銷毀 Bean 前執(zhí)行)");
}
public void myDestroy() {
System.out.println("執(zhí)行了 myDestroy (銷毀 Bean 前執(zhí)行)");
}
public void sayHi(){
System.out.println("使用 Bean");
}
}
Spring 配置文件,將 Bean 手動存儲在 Spring 容器中,要注意init-method
,destroy-method
這兩個屬性的值要和上面實現(xiàn)的方法名對應(yīng)。
<bean id="mybean" class="BeanLifeComponent"
init-method="myInit" destroy-method="myDestroy">
</bean>
測試代碼:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanLifeTest {
public static void main(String[] args) {
// 父類 ApplicationContext 中沒有 close, destroy 系列方法
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
BeanLifeComponent component =
context.getBean("mybean",BeanLifeComponent.class);
component.sayHi();
// 關(guān)閉容器
context.close();
}
}
運行結(jié)果:
如果代碼中飄紅說 @PostConstruct 和 @PreDestroy 注解如果找不到,需要導(dǎo)入下面的jar包。
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version></dependency>
找不到的原因是,從 JDK9 以后 jdk 中的 javax.annotation 包被移除了,這兩個注解剛好就在這個包中。
4. 相關(guān)注解總結(jié)
@Scope
名稱 | @Scope |
---|---|
類型 | 類注解 |
位置 | 類定義上方 |
作用 | 設(shè)置該類創(chuàng)建對象的作用范圍 可用于設(shè)置創(chuàng)建出的bean是否為單例對象 |
屬性 | value(默認(rèn)):定義bean作用范圍, 默認(rèn)值singleton(單例),可選值prototype(非單例) |
@PostConstruct
名稱 | @PostConstruct |
---|---|
類型 | 方法注解 |
位置 | 方法上 |
作用 | 設(shè)置該方法為初始化方法 |
屬性 | 無 |
@PreDestroy
名稱 | @PreDestroy |
---|---|
類型 | 方法注解 |
位置 | 方法上 |
作用 | 設(shè)置該方法為銷毀方法 |
屬性 | 無 |
注解功能與 XML 實現(xiàn)對應(yīng)關(guān)系:文章來源:http://www.zghlxwxcb.cn/news/detail-651678.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-651678.html
到了這里,關(guān)于Spring Bean的作用域和生命周期的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!