?哈嘍,哈嘍,大家好~ 我是你們的老朋友:保護(hù)小周???
今天給大家?guī)?lái)的是 Spring 項(xiàng)目的執(zhí)行流程解析?和 Bean 對(duì)象的6 種作用域以及生命周期,本文將為大家講解,一起來(lái)看看叭~
本期收錄于博主的專欄:JavaEE_保護(hù)小周?的博客-CSDN博客
適用于編程初學(xué)者,感興趣的朋友們可以訂閱,查看其它 “JavaEE基礎(chǔ)知識(shí)”。
更多精彩敬請(qǐng)期待:保護(hù)小周? *★,°*:.☆( ̄▽ ̄)/$:*.°★* ‘
一、Spring 的執(zhí)行流程?
Bean 執(zhí)行流程(Spring 執(zhí)行流程):?jiǎn)?dòng) Spring 容器 -> 讀取Spring 配置文件->?實(shí)例化 Bean 對(duì)象(分配內(nèi)存空間,從無(wú)到有) -> Bean 注冊(cè)到 Spring 中(存操作) -> 將 Bean 注入到需要的類中(取操作)。
?Spring 注解相關(guān)操作感興趣得老鐵可以閱讀博主得另一篇博客:
【Spring】使用注解讀取和存儲(chǔ)Bean對(duì)象_保護(hù)小周?的博客-CSDN博客
二、Bean 的作用域
Spring 是一個(gè)包含眾多工具方法的 IoC 容器,也可以認(rèn)為 Spring 就是用來(lái)讀取和存儲(chǔ) Bean 對(duì)象的,那么 Bean 對(duì)象就是Spring 中一個(gè)至關(guān)重要的角色。
2.1 通過(guò)案例來(lái)觀察 Spring 中Bean 對(duì)象默認(rèn)的作用域
設(shè)計(jì)一個(gè)公共的 Student 類的Bean對(duì)象,注冊(cè)到 Spring 容器中,提供給? A用戶 和 B用戶使用,A用戶不講武德悄悄地修改 Student 類型Bean 對(duì)象的數(shù)據(jù),B 用戶在使用的 Bean 對(duì)象的時(shí)候發(fā)現(xiàn)數(shù)據(jù)已經(jīng)被篡改了,相當(dāng)?shù)膼阑饉
Student 類如下:
@Data
public class Student {
private int id; // 學(xué)號(hào)
private String name; // 姓名
private int age; // 年齡
private String sex; // 性別
}
給大家介紹一個(gè)第三方的框架:?lombok?
"Lombok"是一個(gè)在Java開(kāi)發(fā)中非常常用的開(kāi)源框架。它的目標(biāo)是通過(guò)自動(dòng)化生成Java代碼的樣板代碼來(lái)簡(jiǎn)化開(kāi)發(fā)過(guò)程,減少冗余代碼,并提高代碼的可讀性。
Lombok提供了一系列的注解,例如@Data、@Getter、@Setter、@NoArgsConstructor等,通過(guò)在類上添加這些注解,可以自動(dòng)生成對(duì)應(yīng)的getter、setter、構(gòu)造函數(shù)等方法。這樣可以大大簡(jiǎn)化Java類的編寫(xiě),提高開(kāi)發(fā)效率。
要使用Lombok,您需要在項(xiàng)目中添加Lombok的依賴,并在IDE中安裝Lombok插件,以便在編譯和運(yùn)行時(shí)正確處理Lombok的注解。
?Lombox 依賴:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
博主使用 @Data 注解就來(lái)源于 lombox ,@Data包含了 @Setter 和 @Getter? 注解,也就是自動(dòng)給Student 類創(chuàng)建 set 和 get 方法。
公共的Bean 對(duì)象,注入到 Spring 中:
@Component
public class Users {
//使用方法注解將 學(xué)生張三注入到 Spring 中
@Bean
public Student getStu() {
Student stu = new Student();
stu.setId(123456);
stu.setName("張三");
stu.setAge(18);
stu.setSex("男");
return stu;
}
}
用戶A 從Spring 中獲取 stu Bean 對(duì)象,并進(jìn)行修改:
@Controller
public class UserA {
//從Spring 中獲取Bean 對(duì)象
@Resource //@Autowired
private Student stu;
public Student getStu() {
System.out.println("用戶A獲取原Bean對(duì)象"+ '\n'+ stu.toString());
//對(duì)原Bean 的數(shù)據(jù)進(jìn)行修改
stu.setName("李四");
stu.setSex("女");
return stu;
}
}
用戶B 從Spring 中獲取 stu Bean 對(duì)象,進(jìn)行使用(打印):
@Controller
public class UserB {
//從Spring 中獲取Bean 對(duì)象
@Resource //@Autowired
private Student stu;
public Student getStu() {
System.out.println("用戶B獲取被用戶A修改過(guò)的 Bean :" + '\n' + stu.toString());
return stu;
}
}
啟動(dòng)類中獲取用戶A 和 用戶B 對(duì)象,調(diào)用getStu() 方法,獲取 stu Bean對(duì)象:
public class App {
public static void main(String[] args) {
//1. 獲取Spring 上下文對(duì)象,里面管理著 bean 對(duì)象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//2. 獲取指定的 Bean 對(duì)象
UserA userA = context.getBean("userA", UserA.class);
Student stuA = userA.getStu();
System.out.println("用戶A:" + stuA.toString());
UserB userB = context.getBean("userB", UserB.class);
Student stuB = userB.getStu();
System.out.println("用戶B:"+ stuB.toString());
}
}
案例分析:
以上操作的的執(zhí)行結(jié)果的原因 Bean 默認(rèn)情況下是單例模式(singleton:一個(gè)類只有一個(gè)實(shí)例),也就是說(shuō)用戶 A 和 用戶 B 操作的都是同一個(gè)Bean 對(duì)象,所以用戶A 修改了唯一的 Bean , 用戶B 獲取到的 Bean 就是被修改后的了。
在Spring 中Bean 的作用域默認(rèn)是singleton 單例模式~
2.2 Bean 對(duì)象的 6 種作用域
限定程序中變量的可用范圍叫做作用域,Bean 對(duì)象的作用域指的是在 Spring 整個(gè)生命周期中的某種行為模式,例如:單例作用域(singleton)的意思就是在整個(gè) Spring 中只有一份實(shí)例,且全局共享,當(dāng)其他人修改了這個(gè)唯一的一份實(shí)例后,另一個(gè)人再獲取到的實(shí)例,就是被修改后的值了。
Spring框架為Bean對(duì)象提供了多種作用域選項(xiàng)。以下是Spring框架中常見(jiàn)的6種Bean對(duì)象作用域:
-
Singleton(單例):在整個(gè)應(yīng)用程序中只存在一個(gè)Bean實(shí)例。每次請(qǐng)求都將返回相同的實(shí)例。獲取Bean(通過(guò)applicationContext.getBean等方法獲取),裝配Bean(即通過(guò)@Autowired,@Resource)
-
Prototype(原型):每次請(qǐng)求將創(chuàng)建一個(gè)新的Bean實(shí)例。每次請(qǐng)求都返回不同的實(shí)例。獲取Bean(通過(guò)applicationContext.getBean等方法獲取),裝配Bean(即通過(guò)@Autowired,@Resource)
-
Request(請(qǐng)求):在每次HTTP請(qǐng)求中,都會(huì)創(chuàng)建一個(gè)新的Bean實(shí)例。每個(gè)HTTP請(qǐng)求都返回不同的實(shí)例。
-
Session(會(huì)話):在用戶會(huì)話期間,都會(huì)創(chuàng)建一個(gè)新的Bean實(shí)例。對(duì)于同一用戶的后續(xù)請(qǐng)求,將返回相同的實(shí)例。
-
Global Session(全局會(huì)話):在整個(gè)應(yīng)用程序的全局會(huì)話期間,都會(huì)創(chuàng)建一個(gè)新的Bean實(shí)例。對(duì)于同一全局會(huì)話的后續(xù)請(qǐng)求,將返回相同的實(shí)例。
-
Application(應(yīng)用程序):在整個(gè)Web應(yīng)用程序的生命周期內(nèi),都會(huì)創(chuàng)建一個(gè)新的Bean實(shí)例。每個(gè)Web應(yīng)用程序都返回相同的實(shí)例。
注意:作用域的使用取決于具體的應(yīng)用場(chǎng)景和需求。Spring框架還支持自定義作用域,普通的Spring 項(xiàng)目只支持 singleton (單例),prototype (多例),剩下的4種狀態(tài)是在 SpringMVC項(xiàng)目中可以設(shè)置。
2.3 給Bean 對(duì)象設(shè)置作用域
使用 @Scope 標(biāo)簽就可以用來(lái)聲明 Bean 的作用域, 如以下代碼所示:
@Component
public class Users {
//聲明為多例模式,每此請(qǐng)求都會(huì)實(shí)例一個(gè)新的Bean對(duì)象
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
//使用方法注解將 學(xué)生張三注入到 Spring 中
@Bean(name = "stu") //給 Bean 對(duì)象起一個(gè)名字
public Student getStu() {
Student stu = new Student();
stu.setId(123456);
stu.setName("張三");
stu.setAge(18);
stu.setSex("男");
return stu;
}
}
@Scope 標(biāo)簽既可以修飾方法也可以修飾類,此處是修飾方法,@Scope 有兩種設(shè)置方式:
1. 直接設(shè)置值: @Scope("prototype")
英語(yǔ)好的朋友可以直接把單詞記住,也是蠻直接方便的昂~
2.使用枚舉設(shè)置:@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
此時(shí)我們給上述例子Student 類型的Bean 對(duì)象設(shè)置了多例模式,這個(gè)時(shí)候運(yùn)行一下看看:
三、Bean 的生命周期
所謂 Bean(對(duì)象) 生命周期指的就是一個(gè)普通對(duì)象從創(chuàng)建到銷毀的整個(gè)過(guò)程,我們把整個(gè)過(guò)程稱之為對(duì)象的生命周期。Bean 對(duì)象也就是一個(gè)普通的實(shí)例對(duì)象。
Bean 對(duì)象的生命周期分為以下五個(gè)部分:
1. 實(shí)例化: Bean (為 Bean對(duì)象分配內(nèi)存空間(堆區(qū)))
2. 設(shè)置屬性:在實(shí)例化后,容器會(huì)將配置的屬性值或引用注入到Bean對(duì)象中,可以通過(guò)構(gòu)造函數(shù)、setter方法或字段注入的方式完成。
3. Bean 對(duì)象的初始化 :Bean對(duì)象的初始化流程通常包括以下幾個(gè)步驟:
加載Bean定義:首先,容器會(huì)讀取配置文件或通過(guò)注解掃描等方式,獲取Bean的定義信息,并將其存儲(chǔ)在內(nèi)存中。
創(chuàng)建Bean實(shí)例:根據(jù)Bean的定義信息,容器會(huì)實(shí)例化Bean對(duì)象。這可以通過(guò)調(diào)用默認(rèn)構(gòu)造函數(shù)或使用工廠方法等方式來(lái)完成。
注入依賴:一旦Bean實(shí)例創(chuàng)建完成,容器會(huì)檢查Bean定義中所聲明的依賴關(guān)系,并將相應(yīng)的依賴注入到Bean實(shí)例中。依賴注入可以通過(guò)構(gòu)造函數(shù)、setter方法或字段注入方式來(lái)實(shí)現(xiàn)。
實(shí)現(xiàn)Aware接口:如果Bean實(shí)現(xiàn)了特定的Aware接口(如BeanNameAware、BeanFactoryAware等),容器會(huì)調(diào)用相應(yīng)的回調(diào)方法,以便提供一些額外的關(guān)于容器的信息。
Bean初始化前的處理:在Bean實(shí)例初始化之前,容器會(huì)調(diào)用BeanPostProcessor接口的實(shí)現(xiàn)類的postProcessBeforeInitialization()方法,允許開(kāi)發(fā)者在初始化之前進(jìn)行一些自定義邏輯的處理。
初始化Bean:實(shí)例化過(guò)程完成后,容器會(huì)根據(jù)Bean定義中的配置調(diào)用初始化方法。這可以是通過(guò)在Bean類中標(biāo)注@PostConstruct注解或?qū)崿F(xiàn)InitializingBean接口的afterPropertiesSet()方法來(lái)完成。
Bean初始化后的處理:在Bean實(shí)例初始化之后,容器會(huì)再次調(diào)用BeanPostProcessor接口的實(shí)現(xiàn)類的postProcessAfterInitialization()方法,允許開(kāi)發(fā)者在初始化之后進(jìn)行一些自定義邏輯的處理。
完成Bean的初始化:至此,Bean的初始化流程完成,可以將其添加到容器中,供其他Bean進(jìn)行依賴注入或其他操作使用。
需要注意的是,以上是一個(gè)典型的Bean對(duì)象初始化流程,實(shí)際應(yīng)用中可能會(huì)有一些差異,例如,使用了代理、繼承了父類等情況會(huì)導(dǎo)致初始化流程有所不同。同時(shí),不同的容器實(shí)現(xiàn)也可能會(huì)有一些細(xì)微的差異。
4. 使用 Bean : Bean 對(duì)象,就是一個(gè)普通的實(shí)例化的對(duì)象,使用都是一樣的,看咋類是咋設(shè)計(jì)的
5. 銷毀 Bean :
銷毀Bean對(duì)象可以通過(guò)以下幾種方式實(shí)現(xiàn):
- 使用Destroy方法:可以在Bean類中實(shí)現(xiàn)DisposableBean接口,并重寫(xiě)其中的destroy()方法。在容器銷毀Bean實(shí)例時(shí),會(huì)自動(dòng)調(diào)用該方法來(lái)銷毀Bean對(duì)象。
public class MyBean implements DisposableBean { // ... @Override public void destroy() { // 銷毀操作 // ... } }
- 使用@PreDestroy注解:可以在Bean類的銷毀方法上添加@PreDestroy注解,當(dāng)容器銷毀Bean實(shí)例時(shí),會(huì)觸發(fā)該注解標(biāo)記的方法進(jìn)行銷毀操作。
public class MyBean { // ... @PreDestroy public void destroy() { // 銷毀操作 // ... } }
- 配置銷毀方法:在配置文件(如XML配置文件)中,可以通過(guò)指定destroy-method屬性來(lái)指定Bean銷毀時(shí)調(diào)用的方法。該方法可以是Bean類中的任意公開(kāi)方法。
<bean id="myBean" class="com.example.MyBean" destroy-method="destroy"> <!-- ... --> </bean>
需要注意的是,銷毀Bean對(duì)象的時(shí)機(jī)由容器管理,通常發(fā)生在容器關(guān)閉時(shí)。如果是單例Bean,則在容器關(guān)閉或手動(dòng)調(diào)用銷毀方法時(shí)觸發(fā)銷毀操作。如果是原型或會(huì)話作用域(session scoped)的Bean,則需要手動(dòng)銷毀。
執(zhí)行流程圖 :
實(shí)例化和初始化的區(qū)別:
實(shí)例化和屬性設(shè)置時(shí) Java 級(jí)別的系統(tǒng) “事件”,操作過(guò)程不可以人為干預(yù),而初始化是給開(kāi)發(fā)者提供的,可以在實(shí)例化之后,類加載完成之前進(jìn)行自定義 “事件” 處理。
總的來(lái)說(shuō),實(shí)例化是創(chuàng)建Bean實(shí)例的過(guò)程,而初始化是對(duì)已創(chuàng)建的Bean對(duì)象進(jìn)行屬性賦值和回調(diào)方法調(diào)用的過(guò)程。實(shí)例化是創(chuàng)建Bean對(duì)象的基礎(chǔ)步驟,而初始化則是在實(shí)例創(chuàng)建完成后對(duì)Bean對(duì)象進(jìn)行配置和準(zhǔn)備的重要步驟。
舉個(gè)例子形象的描述Bean 對(duì)象的生命周期:買房子
1. 實(shí)例化:在某市蓋了一套毛坯房(從無(wú)到有,將字節(jié)碼轉(zhuǎn)換為內(nèi)存中的對(duì)象,只是分配了內(nèi)存)
2. 設(shè)置屬性:(容器會(huì)將配置的屬性值或引用注入到Bean對(duì)象中)。購(gòu)買裝修材料,引入外部資源。
3. 初始化:給房子裝修~
- 各種通知:聯(lián)系各種裝修師傅,水工,電工,瓦工……
- 初始化的前置工作:裝修師傅現(xiàn)場(chǎng)考察毛坯房的環(huán)境,制定裝修方案。
- 進(jìn)行初始化工作:(@PostConstruct 初始化、init-method 初始化),裝修師傅進(jìn)行裝修,其中有裝修技術(shù)好的,有裝修技術(shù)差的。
- 初始化的后置工作: 裝修完畢之后對(duì)房子進(jìn)行清理。
4. 使用 Bean 對(duì)象: 拎包入住~
5. 銷毀 Bean 對(duì)象: 拆,或者賣掉這套房子~
一定是先設(shè)置屬性,再進(jìn)行初始化操作。
好了,到這里,【Spring 】執(zhí)行流程解析:了解Bean的作用域及生命周期?博主已經(jīng)分享完了,希望對(duì)大家有所幫助,如有不妥之處歡迎批評(píng)指正。?
?
下期預(yù)告:【SpringBoot】框架~
感謝每一位觀看本篇文章的朋友,更多精彩敬請(qǐng)期待:保護(hù)小周? *★,°*:.☆( ̄▽ ̄)/$:*.°★*?文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-595455.html
遇見(jiàn)你,所有的星星都落在我的頭上……?文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-595455.html
到了這里,關(guān)于【Spring 】執(zhí)行流程解析:了解Bean的作用域及生命周期的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!