Java開發(fā)學(xué)習(xí)之Bean的作用域和生命周期詳解
一、Bean 的作用域
在之前學(xué)習(xí)Java基礎(chǔ)的時候,有接觸到作用域這樣的概念。一個變量并不一定在任何區(qū)域都是有效的,限定這個變量的可用性的代碼范圍就是該變量的作用域。
但是在這里 Bean 的作用域的概念和以前所認(rèn)為的作用域有所不同。
Bean 的作用域是指 Bean 在 Spring 整個框架中的某種行為模式。
接下來,將會舉一個案例來講講什么是作用域,什么是行為模式
案例概要:
創(chuàng)建一個共有的 Bean ,使用者A和使用者B都對該 Bean 進(jìn)行了使用
使用者A在進(jìn)行使用的時候,創(chuàng)建一個新的變量接收注入進(jìn)來的 Bean,進(jìn)行修改,將修改后的結(jié)果進(jìn)行返回;
使用者B 直接將注入進(jìn)來的 Bean 進(jìn)行返回,不進(jìn)行任何操作
代碼實(shí)現(xiàn):
步驟一:創(chuàng)建出一個公共的 Bean
@Component
public class UserComponent {
@Bean
public User getUser() {
User user = new User();
user.setId(1);
user.setName("張三");
user.setPassWord("111111");
return user;
}
}
步驟二:使用者A獲取到公共 Bean,進(jìn)行修改
@Controller
public class UserControllerA {
@Autowired
private User user;
public User getUser1() {
System.out.println("使用者A拿到的原始user:" + user);
User myUser = user;
myUser.setName("李四");
return myUser;
}
}
步驟三:使用者B直接將獲取到的公共的 Bean 進(jìn)行返回
@Controller
public class UserControllerB {
@Autowired
private User user;
public User getUser2() {
return user;
}
}
步驟四:main 中獲取 UserControllerA 類和 UserControllerB 類使用查看
public class Start {
public static void main(String[] args) {
//獲取 Spring 上下文
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
//獲取到Spring容器中的 UserControllerA 類(Bean 對象)
UserControllerA userControllerA = context.getBean("userControllerA",UserControllerA.class);
//使用 Bean 對象
System.out.println("使用者A->"+userControllerA.getUser1());
//獲取到Spring容器中的 UserControllerA 類(Bean 對象)
UserControllerB userControllerB = context.getBean("userControllerB",UserControllerB.class);
//使用 Bean 對象
System.out.println("使用者B->"+userControllerB.getUser2());
}
}
預(yù)期結(jié)果:
使用者 A修改后,其結(jié)果是修改過的;使用者 B 沒有修改過,其結(jié)果應(yīng)該和原始user一樣
結(jié)果顯示:

和預(yù)期結(jié)果有所不同,使用者 A 和使用者 B 所得到的結(jié)果都是被修改過的。
這是因?yàn)樵?Spring 中,Bean 默認(rèn)情況下是單例狀態(tài),大家用的都是同一份對象,是全局共享的,當(dāng)有其他人修改了該對象,另一個人所獲取到的對象就是被修改過的,這便是 Bean 六大作用域之一——單例作用域(singleton)
在寫 WEB 項(xiàng)目的時候,我們知道 DataSource 就是單例模式,使用單例,好處多多,可以確保所有對象都訪問唯一實(shí)例,而且減少了內(nèi)存的開支和系統(tǒng)性能的開銷,因此 Bean 默認(rèn)情況下是單例狀態(tài)。
若想要按照預(yù)期結(jié)果輸出,就需要將 Bean 的作用域設(shè)置成原型作用域,即無論誰來使用 Bean 對象,獲取到的都是原始的 Bean 對象,大家各玩各的。
需要在注入的對象上使用注解修改作用域,有以下兩種方法(Scope就是作用域的意思)
- 直接將作用域以 String 類型寫到()中
- 使用全局參數(shù),類似于枚舉
@Component
public class UserComponent {
//@Scope("prototype") //方法一
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) //方法二
@Bean
public User getUser() {
User user = new User();
user.setId(1);
user.setName("張三");
user.setPassWord("111111");
return user;
}
}
Bean 的 6 種作用域
singleton:單例作用域
- Bean 在 IoC 容器中只存在一個實(shí)例
- 當(dāng) Bean 對象屬性狀態(tài)無需更新時使用該作用域
- Spring 支持
prototype:原型作?域/多例作?域
- 每次獲取該 Bean 時都會創(chuàng)建新的實(shí)例,即獲取的都是原始的 Bean
- 當(dāng) Bean 對象屬性狀態(tài)會更新時使用該作用域
- Spring 支持
request:請求作用域
- 每次 HTTP 請求都會創(chuàng)建新的 Bean 實(shí)例
- 一次 HTTP 請求和響應(yīng)共享的 Bean
- 限定 SpringMVC
session:會話作用域
- 在一個 HTTP session中,創(chuàng)建一個 Bean 實(shí)例
- 用戶會話共享一個 Bean
- 限定 SpringMVC
application:全局作用域
- 在一個 HTTP Servlet Context中,創(chuàng)建一個 Bean 實(shí)例
- 一個 WEB 上下文中共享一個 Bean
- 限定 SpringMVC
websocket: HTTP WebSocket 作?域(不常用)
- 在一個 HTTP WebSocket 中,創(chuàng)建一個 Bean 實(shí)例
- 限定 Spring WebSocket
單例作用域和全局作用域比較像,但全局作用域范圍沒有單例作用域大,前者是 Spring 核心的作用域,后者是 Spring Web 中的作用域,前者作用于 IoC 容器,后者作用于 Servlet 容器
二、Spring 的執(zhí)行流程
Spring 的執(zhí)行流程也可以說是 Bean的執(zhí)行流程,主要分成4部分
- 啟動 Spring 容器
- 加載 XML 文件,實(shí)例化 Bean(進(jìn)行內(nèi)存的分配)
- Bean 存到 Spring 容器中(五大類注解,方法注解)
- 將存儲的 Bean 中的注入的對象屬性進(jìn)行初始化,即進(jìn)行裝配(取出 Bean)

三、Bean 的生命周期
Bean 的生命周期即 Bean 從誕生到銷毀的整個過程
實(shí)例化 Bean 對象,申請內(nèi)存空間
設(shè)置 Bean 的屬性,進(jìn)行依賴注入和裝配
Bean 的初始化
- 各種 Aware 感知:BeanNameAware、BeanFactoryAware、ApplicationContextAware、…
- 執(zhí)? BeanPostProcessor 初始化前置?法
- 初始化方法:構(gòu)造器方法
@PostConstructor(對象加載完依賴注入后執(zhí)行) - 初始化方法:
init-method - 執(zhí)? BeanPostProcessor 初始化后置?法
使用 Bean
銷毀 Bean
- 銷毀方法:
@PreDestroy - 接口方法:
DisposableBean - 銷毀方法:
destroy-method
補(bǔ)充
實(shí)例化和初始化的區(qū)別
- 實(shí)例化:這里的實(shí)例化和從前的實(shí)例化對象是有區(qū)別的,這里的實(shí)例化就負(fù)責(zé)內(nèi)存的分配,一個從無到有的過程。實(shí)例化和屬性設(shè)置時 Java 級別的系統(tǒng)“事件”,操作過程是不可人工干預(yù)的
- 初始化:是對 Bean 進(jìn)行填充的過程,程序員可以進(jìn)行介入,真正的將 Bean 放到 Spring 容器中
@PostConstructor 和 @PreDestroy 是注解方式進(jìn)行初始化和注銷,init-method 和 destroy-method 是 XML 方法進(jìn)行初始化和注銷,一般只要使用其中的一種進(jìn)行初始化
設(shè)置屬性一定要在初始化之前,因?yàn)槌跏蓟部赡苄枰褂玫阶⑷氲膶ο?,如果沒有進(jìn)行屬性的設(shè)置,初始化就會出現(xiàn)問題
案例:生命周期演示
@Component
public class BeanLife implements BeanNameAware {
@Override
public void setBeanName(String s) {
System.out.println("BeanName 感知:"+ s);
}
@PostConstruct
public void postConstructor() {
System.out.println("執(zhí)行初始化方法:PostConstructor");
}
@PreDestroy
public void preDestroy() {
System.out.println("執(zhí)行銷毀方法:PreDestroy");
}
public void initMethod() {
System.out.println("執(zhí)行初始化方法:init-method");
}
public void destroyMethod() {
System.out.println("執(zhí)行銷毀方法:destroy-method");
}
}
XML
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:content="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置一下:bean注解掃描的根路徑(方面后面更簡單存儲對象到spring容器)-->
<content:component-scan base-package="com.bit.beans"></content:component-scan>
<beans>
<bean id="beanLife" class="com.bit.beans.Component.BeanLife" init-method="initMethod"
destroy-method="destroyMethod"></bean>
</beans>
</beans>
調(diào)用
public class Start {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new
ClassPathXmlApplicationContext("spring.xml");
BeanLife beanLife = context.getBean("beanLife",BeanLife.class);
System.out.println("---使用 Bean 對象---");
System.out.println("---注銷 Bean 對象--- ");
context.destroy();//容器的銷毀相當(dāng)于銷毀所有的 Bean
}
}
結(jié)果顯示:

流程圖展示:

到此這篇關(guān)于Java開發(fā)學(xué)習(xí)之Bean的作用域和生命周期詳解的文章就介紹到這了,更多相關(guān)Bean作用域和生命周期內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Mybatis中SqlSession接口中selectList方法詳解
這篇文章主要給大家介紹了關(guān)于Mybatis中SqlSession接口中selectList方法的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2023-03-03
安卓系統(tǒng)中實(shí)現(xiàn)搖一搖畫面振動效果的方法
這篇文章主要介紹了安卓系統(tǒng)中實(shí)現(xiàn)搖一搖畫面振動效果的方法,調(diào)用Android SDK中的SensorEventListener接口,需要的朋友可以參考下2015-07-07
SpringBoot如何配置MySQL和Oracl雙數(shù)據(jù)源(Mybatis)
這篇文章主要介紹了SpringBoot如何配置MySQL和Oracl雙數(shù)據(jù)源(Mybatis)問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03
java.net.http.HttpClient使用示例解析
這篇文章主要為大家介紹了java.net.http.HttpClient使用示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
java 將 list 字符串用逗號隔開拼接字符串的多種方法
這篇文章主要介紹了java 將 list 字符串用逗號隔開拼接字符串,本文給大家分享四種方法,每種方法通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-12-12
Java知識點(diǎn)歸納 —給Java新手的一些建議(新手必看)
以下簡單介紹了下我對于這些java基本知識點(diǎn)和技術(shù)點(diǎn)的一些看法和心得,這些內(nèi)容都源自于我這些年來使用java的一些總結(jié)2016-05-05

