Spring學習總結(五):基於XML裝配Bean(3)
一、bean的生命週期
所謂生命週期,就是指物件從建立到銷燬的整個過程。Spring容器可以管理bean的生命週期,當一個bean被載入到Spring容器中,它就具有了生命,並且Spring在確保bean被使用之前,進行一些其他的操作。
bean的生命週期分為以下幾個過程:
(1)通過構造器建立bean例項(無參構造)。
(2)為bean的屬性設定值和對其他bean的引用(呼叫set方法)。
(3)呼叫bean的初始化方法(需要配置初始化方法——init-method屬性)。
(4)使用bean(獲取到物件)。
(5)當容器關閉的時候,呼叫bean的銷燬方法(需要配置銷燬的方法——destory-method屬性)。
接下來,用程式碼的方式來對上述流程進行驗證。
(1)建立Student類。
1)為驗證第一步和第二步,需要在無參構造和set方法中加入輸出語句。
2)分別定義initMethod()和destoryMethod()方法,並加入輸出語句。
public class Student { private String name; public Student(){ System.out.println("第一步:呼叫無參構造建立物件"); } public void setName(String name) { System.out.println("第二步:呼叫set方法注入屬性值"); this.name = name; } public void initMethod(){ System.out.println("第三步:呼叫初始化方法"); } public void destoryMethod(){ System.out.println("第五步:執行完畢,呼叫銷燬方法"); } }
(2)在bean.xml中進行配置。
<!-- 屬性說明: init-method:實體類中初始化的方法。 destroy-method:實體類中銷燬的方法 --> <bean id="student" class="com.yht.example3.entity.Student" init-method="initMethod" destroy-method="destoryMethod"> <property name="name" value="YHT"></property> </bean>
(3)進行測試。
@Test
public void testLive(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Student student = context.getBean("student", Student.class);
System.out.println("第四步:獲取到物件例項 \t" + student);
// ApplicationContext中沒有close()方法,而是在子類ClassPathXmlApplicationContext中
context.close();
}
執行結果如下:
以上就是Spring中bean的生命週期的五個過程,除此之外,Spring中bean在初始化前後還有兩個過程,我們通過bean的後置處理器來完成。
(1)定義MyBeanPost,實現BeanPostProcessor介面,並覆寫其中的方法。
public class MyBeanPost implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("我在初始化之前執行");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("我在初始化之後執行");
return bean;
}
}
(2)在bean.xml中配置myBeanPost。
<bean id="myBeanPost" class="com.yht.example3.posthandler.MyBeanPost"></bean>
(3)進行單元測試,得到結果如下:
由此可以得到bean的生命週期的七個步驟:
(1)通過構造器建立bean例項(無參構造)。
(2)為bean的屬性設定值和對其他bean的引用(呼叫set方法)。
(3)在初始化之前執行的操作。
(4)呼叫bean的初始化方法(需要配置初始化方法——init-method屬性)。
(5)在初始化之後執行的操作。
(6)使用bean(獲取到物件)。
(7)當容器關閉的時候,呼叫bean的銷燬方法(需要配置銷燬的方法——destory-method屬性)。
二、bean的作用域
Spring在初始化一個bean的例項時,可以指定該例項的作用域。在Spring中定義了五種作用域,我們常用的是singleton(單例)和prototype(多例),具體介紹如下:
屬性 | 說明 |
---|---|
singleton | 單例模式,使用 singleton 定義的 Bean 在 Spring 容器中只有一個例項,這也是 Bean 預設的作用域 |
prototype | 原型模式,每次通過 Spring 容器獲取 prototype 定義的 Bean 時,容器都將建立一個新的 Bean 例項 |
request | 在一次 HTTP 請求中,容器會返回該 Bean 的同一個例項。而對不同的 HTTP 請求,會返回不同的例項,該作用域僅在當前 HTTP Request 內有效 |
session | 在一次 HTTP Session 中,容器會返回該 Bean 的同一個例項。而對不同的 HTTP 請求,會返回不同的例項,該作用域僅在當前 HTTP Session 內有效 |
global session | 在一個全域性的 HTTP Session 中,容器會返回該 Bean 的同一個例項。該作用域僅在使用 portlet context 時有效 |
1、singleton
singleton 是 Spring 容器預設的作用域,當一個 Bean 的作用域為 singleton 時,Spring 容器中只會存在一個共享的 Bean 例項,並且所有對 Bean 的請求,只要 id 與該 Bean 定義相匹配,就只會返回 Bean 的同一個例項。
在 Spring 配置檔案中,可以使用 <bean> 元素的 scope 屬性,將 Bean 的作用域定義成 singleton,其配置方式如下所示:
<bean id="teacher" class="com.yht.example3.entity.Teacher" scope="singleton"></bean>
用下面程式發現兩次獲取得teacher地址一致,這說明此時的teacher就是單例的,全域性唯一。
@Test
public void testTeacher(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Teacher teacher1 = context.getBean("teacher", Teacher.class);
Teacher teacher2 = context.getBean("teacher", Teacher.class);
System.out.println("teacher1的地址 : " + teacher1);
System.out.println("teacher2的地址 : " + teacher2);
}
2、prototype
使用 prototype 作用域的 Bean 會在每次請求該 Bean 時都會建立一個新的 Bean 例項。因此對需要保持會話狀態的 Bean(如 Structs 2的 Action 類)應該使用 prototype 作用域。將上述例子中scope屬性改為prototype,並進行測試,會發現兩次得到的結果不一致,這說明物件是多例的。
<bean id="teacher" class="com.yht.example3.entity.Teacher" scope="prototype"></bean>
執行結果如下:
3、singleton和prototype的區別
區別一:singleton是單例,prototype是多例。
區別二:當socpe設定為singleton時,載入Spring配置檔案時就會建立單例物件。當socpe設定為prototype時,是在呼叫getBean時才會建立物件。
三、bean的三種例項化方式
在Spring中,要想使用容器中的Bean,也需要例項化Bean。例項化Bean有三種方式,分別是:構造器例項化、靜態工廠例項化、例項工廠方式例項化。構造器例項化是最常用的一種方式,其他方式瞭解即可。
1、構造器例項化
構造器例項化是指Spring容器通過Bean對應類中預設的無參構造方法來例項化Bean。
(1)建立Teacher類
public class Teacher {
private String name;
private String major;
public void setName(String name) {
this.name = name;
}
public void setMajor(String major) {
this.major = major;
}
public void showMessage(){
System.out.println("教師:" + this.name + ",方向:" + this.major);
}
}
(2)在bean.xml中配置。
<bean id="teacher1" class="com.yht.example3.entity.Teacher">
<property name="name" value="趙茜"></property>
<property name="major" value="網路"></property>
</bean>
(3)進行單元測試。
@Test
public void testCommon(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Teacher teacher = context.getBean("teacher1", Teacher.class);
teacher.showMessage();//教師:趙茜,方向:網路
}
2、靜態工廠例項化
(1)建立一個MyStaticFactory類,並建立一個靜態方法createObject()來返回Teacher例項。
public class MyStaticFactory {
public static Teacher createObject(){
return new Teacher();
}
}
(2)在bean.xml中進行配置。
<!-- 靜態工廠例項化 -->
<!--
class:靜態工廠的類。
factory-method:靜態工廠中的方法
-->
<bean id="teacher2" class="com.yht.example3.factory.MyStaticFactory" factory-method="createObject">
<property name="name" value="孫飛"></property>
<property name="major" value="人工智慧"></property>
</bean>
(3)進行的單元測試。
@Test
public void testStatic(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Teacher teacher = context.getBean("teacher2", Teacher.class);
teacher.showMessage();//教師:孫飛,方向:人工智慧
}
3、例項工廠例項化
(1)建立一個MyCommonFactory類,並建立一個方法createObject()來返回Teacher例項。
public class MyCommonFactory {
public Teacher createObject(){
return new Teacher();
}
}
(2)在bean.xml中進行配置。
<!-- 例項工廠例項化 -->
<!--
factory-bean :指向配置的例項工廠
factory-method:靜態工廠中的方法
-->
<bean id="teacher3" factory-bean="teacherFactory" factory-method="createObject">
<property name="name" value="郭安"></property>
<property name="major" value="車聯網"></property>
</bean>
<!-- 配置工廠 -->
<bean id="teacherFactory" class="com.yht.example3.factory.MyCommonFactory"></bean>
(3)進行的單元測試。
@Test
public void testInstance(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
Teacher teacher = context.getBean("teacher3", Teacher.class);
teacher.showMessage();//教師:郭安,方向:車聯網
}
4、三種例項化方式的區別
構造器例項化:通過無參構造的方法例項化Bean,其實質是將Bean對應的類交給Spring自帶的工廠(BeanFactory)管理,由Spring自帶的工廠模式幫我們建立和維護這個類。
靜態工廠方式例項化:通過靜態工廠建立並返回Bean。其實質是將Bean對應的類交給我們自己的靜態工廠管理。Spring只是幫我們呼叫了靜態工廠建立例項的方法。運用:很多時候我們在使用第三方jar包提供給我們的類時,由於這個類沒有構造方法,是通過第三方包提供的靜態工廠建立的,如果我們想把第三方jar包裡面的這個類交由Spring來管理的話,就可以使用Spring提供的靜態工廠建立例項的配置。
例項工廠方式例項化:通過例項工廠建立並返回Bean,其實質就是把建立例項的工廠類和呼叫工廠類的方法建立例項的方法的過程也交由Spring管理,建立例項的這個過程也是由我們自己配置的例項工廠內部實現的。運用:如Spring整合Hibernate就是通過這種方式實現的。但對於沒有與Spring整合過的工廠類,我們一般都是自己用程式碼來管理的。