Bean作用域問(wèn)題案例分析
假設(shè)現(xiàn)在有?個(gè)公共的 Bean,提供給 A ?戶和 B ?戶使?,然?在使?的途中 A ?戶卻“悄悄”地修改了公共 Bean 的數(shù)據(jù),導(dǎo)致 B ?戶在使?時(shí)發(fā)?了預(yù)期之外的邏輯錯(cuò)誤。
公共 Bean
@Component
public class DogBean {
@Bean
public Dog dog() {
Dog dog = new Dog();
dog.setName("旺財(cái)");
dog.setId(1);
dog.setAge(5);
return dog;
}
}
A 用戶使用時(shí)修改
@Controller
public class ScopeController {
@Autowired
private Dog dog;
public void doScope(){
System.out.println("do scope controller");
System.out.println("原始dog對(duì)象:" + dog.toString());
Dog dog2 = dog;
dog2.setAge(10);
dog2.setId(2);
dog2.setName("小黑");
System.out.println("修改后的dog對(duì)象:" + dog.toString());
}
}
B 用戶使用時(shí)
@Controller
public class ScopeController2 {
@Resource
private Dog dog;
public void doScope(){
System.out.println("do scope controller2");
System.out.println(dog.toString());
}
}
原因分析
我們可以看到,B 用戶在使用這個(gè)Bean對(duì)象時(shí),得到的Dog是被A 用戶修改過(guò)的,這無(wú)疑會(huì)給 B 用戶帶來(lái)很大的麻煩。操作以上問(wèn)題的原因是因?yàn)?Bean 默認(rèn)情況下是單例狀態(tài)(singleton),也就是所有?的使?的都是同?個(gè)對(duì)象,之前我們學(xué)單例模式的時(shí)候都知道,使?單例可以很?程度上提?性能,所以在 Spring 中 Bean 的作?域默認(rèn)也是 singleton 單例模式。
作用域定義
限定程序中變量的可?范圍叫做作?域,或者說(shuō)在源代碼中定義變量的某個(gè)區(qū)域就叫做作?域。? Bean 的作?域是指 Bean 在 Spring 整個(gè)框架中的某種?為模式,?如 singleton 單例作?域,就表示 Bean 在整個(gè) Spring 中只有?份,它是全局共享的,那么當(dāng)其他?修改了這個(gè)值之后,那么另?個(gè)?讀取到的就是被修改的值。
Bean 的6種作用域
Spring 容器在初始化?個(gè) Bean 的實(shí)例時(shí),同時(shí)會(huì)指定該實(shí)例的作?域。Spring有 6 種作?域,最后
四種是基于 Spring MVC ?效的:
- singleton:?jiǎn)卫?域
- prototype:原型作?域(多例作?域)
- request:請(qǐng)求作?域
- session:回話作?域
- application:全局作?域
- websocket:HTTP WebSocket 作?域
后4種狀態(tài)是Spring MVC 中的值,在普通的 Spring 項(xiàng)目中只有前兩種。
singleton
- 官?說(shuō)明:(Default) Scopes a single bean definition to a single object instance for each Spring IoC container.
- 描述:該作?域下的Bean在IoC容器中只存在?個(gè)實(shí)例:獲取Bean(即通過(guò)applicationContext.getBean等?法獲取)及裝配Bean(即通過(guò)@Autowired注?)都是同?個(gè)對(duì)象。
- 場(chǎng)景:通常?狀態(tài)的Bean使?該作?域。?狀態(tài)表示Bean對(duì)象的屬性狀態(tài)不需要更新
- 備注:?jiǎn)卫J侥J(rèn)的作用域,只有一個(gè)全局對(duì)象
prototype
- 官?說(shuō)明:Scopes a single bean definition to any number of object instances.
- 描述:每次對(duì)該作?域下的Bean的請(qǐng)求都會(huì)創(chuàng)建新的實(shí)例:獲取Bean(即通過(guò)applicationContext.getBean等?法獲?。┘把b配Bean(即通過(guò)@Autowired注?)都是新的對(duì)象實(shí)例。
- 場(chǎng)景:通常有狀態(tài)的Bean使?該作?域
- 備注:原型模式【多例默認(rèn)】,每次訪問(wèn)都會(huì)創(chuàng)建一個(gè)新對(duì)象
request
- 官?說(shuō)明:Scopes a single bean definition to the lifecycle of a single HTTP request. That is,each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.
- 描述:每次http請(qǐng)求會(huì)創(chuàng)建新的Bean實(shí)例,類似于prototype
- 場(chǎng)景:?次http的請(qǐng)求和響應(yīng)的共享Bean
- 備注:限定SpringMVC中使?
session
- 官?說(shuō)明:Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.
- 描述:在?個(gè)http session中,定義?個(gè)Bean實(shí)例
- 場(chǎng)景:?戶回話的共享Bean, ?如:記錄?個(gè)?戶的登陸信息
- 備注:限定SpringMVC中使?
application
- 官?說(shuō)明:Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext.
- 描述:在?個(gè)http servlet Context中,定義?個(gè)Bean實(shí)例
- 場(chǎng)景:Web應(yīng)?的上下?信息,?如:記錄?個(gè)應(yīng)?的共享信息
- 備注:限定SpringMVC中使?
websocket
- 官?說(shuō)明:Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext.
- 描述:在?個(gè)HTTP WebSocket的?命周期中,定義?個(gè)Bean實(shí)例
- 場(chǎng)景:WebSocket的每次會(huì)話中,保存了?個(gè)Map結(jié)構(gòu)的頭信息,將?來(lái)包裹客戶端消息頭。第?次初始化后,直到WebSocket結(jié)束都是同?個(gè)Bean。
- 備注:限定Spring WebSocket中使?
設(shè)置作用域
使? @Scope 標(biāo)簽就可以?來(lái)聲明 Bean 的作?域,@Scope 標(biāo)簽既可以修飾?法也可以修飾類,@Scope 有兩種設(shè)置?式:
- 直接設(shè)置值:@Scope(“prototype”)
- 使?枚舉設(shè)置:@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Component
public class DogBean {
@Scope("prototype")
@Bean
public Dog dog() {
Dog dog = new Dog();
dog.setName("旺財(cái)");
dog.setId(1);
dog.setAge(5);
return dog;
}
}
這樣設(shè)置完成后,A 用戶的修改對(duì) B 用戶的使用就不會(huì)造成影響了。
Spring 執(zhí)行流程
1、啟動(dòng)容器
2、Bean 初始化
配置文件中的bean、配置了加載組件路徑下的類進(jìn)行掃描【看有沒(méi)有類注解】
3、注冊(cè)Bean對(duì)象到容器中
通過(guò)五大注解來(lái)把對(duì)象注入到容器中,并且只有在包掃描路徑上的類,且使用Spring的注解才可以被注冊(cè)到容器中。
具體參考博客:使用spring注解儲(chǔ)存對(duì)象
4、裝配Bean屬性
如果Bean對(duì)象 需要使用其他Bean對(duì)象作為屬性,可以使用注解@Autowired、@Resource
具體參考博客:spring依賴注入
Bean 生命周期
1、實(shí)例化
這是生命周期的第一步。在這個(gè)階段,Spring會(huì)創(chuàng)建一個(gè)Bean的實(shí)例。這就像在工廠里制造一個(gè)產(chǎn)品一樣,但在這里,Spring負(fù)責(zé)創(chuàng)建和管理Bean對(duì)象。
2、設(shè)置屬性
一旦Bean實(shí)例化了,Spring會(huì)通過(guò)依賴注入的方式設(shè)置Bean的屬性。這就像給產(chǎn)品添加特性和功能一樣。你可以在配置文件中指定屬性值,然后Spring會(huì)把這些值設(shè)置給Bean。
3、初始化
在這個(gè)階段,Bean被初始化。你可以定義初始化方法,Spring會(huì)在設(shè)置屬性后調(diào)用這些方法。這允許你在Bean準(zhǔn)備好之前做一些額外的設(shè)置或者操作。
- 執(zhí)行各種通知: 實(shí)現(xiàn)了各種 Aware 通知的方法,如 BeanNameAware、BeanFactoryAware、ApplicationContextAware 的接口方法;
- BeanPostProcessor的前置處理(postProcessBeforeInitialization): 如果在應(yīng)用中定義了BeanPostProcessor接口的實(shí)現(xiàn)類,Spring會(huì)在Bean初始化之前調(diào)用這些實(shí)現(xiàn)類的postProcessBeforeInitialization方法。這提供了一個(gè)機(jī)會(huì),你可以在Bean被初始化之前進(jìn)行一些定制化的操作。
- 初始化方法(InitializingBean和init-method): 如果Bean實(shí)現(xiàn)了InitializingBean接口,Spring會(huì)在屬性設(shè)置后調(diào)用它的afterPropertiesSet方法。執(zhí)? @PostConstruct 初始化?法,依賴注?操作之后被執(zhí)?。此外,你還可以通過(guò)配置指定一個(gè)自定義的初始化方法(通常使用init-method屬性)。在這個(gè)方法中,你可以執(zhí)行任何你需要在Bean初始化時(shí)完成的邏輯。
- BeanPostProcessor的后置處理(postProcessAfterInitialization): 類似于前置處理,如果有BeanPostProcessor接口的實(shí)現(xiàn)類,Spring會(huì)在Bean初始化之后調(diào)用這些實(shí)現(xiàn)類的postProcessAfterInitialization方法。這個(gè)階段可以用來(lái)進(jìn)行一些額外的操作,例如修改Bean的屬性或狀態(tài)。
4、使用Bean
一旦Bean被初始化,它就準(zhǔn)備好被使用了。你可以在應(yīng)用程序的其他部分中引用并使用這個(gè)Bean,執(zhí)行你所需的操作。這就像使用你制造的產(chǎn)品一樣。
5、銷毀Bean
當(dāng)應(yīng)用程序關(guān)閉或者不再需要Bean時(shí),Spring會(huì)執(zhí)行銷毀操作。你可以定義銷毀方法,在Bean不再需要時(shí)執(zhí)行一些清理工作,比如關(guān)閉數(shù)據(jù)庫(kù)連接或者釋放資源。
生命周期演示
package com.fyd.controller;
import org.springframework.beans.factory.BeanNameAware;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class BeanLifeComponent implements BeanNameAware {
@Override
public void setBeanName(String name) {
System.out.println("執(zhí)行了 Bean Name 通知" + name);
}
public void init(){
System.out.println("執(zhí)行了 init 方法");
}
@PostConstruct
public void myPostConstruct(){
System.out.println("執(zhí)行了 myPostConstruct 方法");
}
/**
* 銷毀前執(zhí)行方法
*/
@PreDestroy
public void myPreDestroy(){
System.out.println("執(zhí)行了 myPreDestroy 方法");
}
public void use(){
System.out.println("執(zhí)行了 use 方法");
}
}
xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd ">
<context:component-scan base-package="com.fyd"/>
<bean id="beanlife" class="com.fyd.controller.BeanLifeComponent"
init-method="init"></bean>
</beans>
調(diào)用代碼文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-657442.html
import com.fyd.controller.BeanLifeComponent;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class BeanLifeTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
BeanLifeComponent beanLifeComponent = context.getBean("beanlife",BeanLifeComponent.class);
beanLifeComponent.use();
// 關(guān)閉容器
context.destroy();
}
}
執(zhí)行結(jié)果:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-657442.html
到了這里,關(guān)于Bean 作用域、生命周期和Spring執(zhí)行流程的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!