上一篇:Spring學(xué)習(xí)筆記(三、IoC)
** Bean常用的配置項(xiàng)**
- Id:在IoC容器中Bean的唯一標(biāo)識(shí)
- Class:具體要實(shí)例化的類
- Scope:作用域
- Constructor arguments:構(gòu)造器參數(shù)
- properties:屬性
- Autowiring mode:自動(dòng)裝配模式
- lazy-initialization mode:懶加載模式
- initialization/destruction method:初始化/銷毀的方法
Bean的作用域
- singleton:?jiǎn)卫敢粋€(gè)Bean容器中只存在一份(默認(rèn))
- prototype:每次請(qǐng)求(每次使用)創(chuàng)建新的實(shí)例,destory方法不生效(因?yàn)橛猛陼?huì)被垃圾回收期回收)
- request:每次http請(qǐng)求創(chuàng)建一個(gè)實(shí)例且僅在當(dāng)前request內(nèi)有效
- session: 每次http請(qǐng)求創(chuàng)建一個(gè)實(shí)例且僅在當(dāng)前session內(nèi)有效
- global session:基于portlet的web中有效(portlet定義了global session),如果是在web中,同session
這里global session有可能還是不懂,特此解釋:舉個(gè)例子,一個(gè)大型的系統(tǒng),有多個(gè)獨(dú)立的模塊組成,他們有一個(gè)統(tǒng)一的登錄入口,登錄進(jìn)入后,用戶可以在整個(gè)系統(tǒng)內(nèi)操作,不需要在登錄其他模塊時(shí),重新登錄。這就是portlet的global session。
下面用例子證明:
-
singleton
在TestDao增加接口:
Paste_Image.png
TestDaoImpl實(shí)現(xiàn):
Paste_Image.png
spring-ioc.xml:
Paste_Image.png
JUnit測(cè)試:
上圖可見,聲明了singleton作用域,即使獲取多次實(shí)例,也依舊是原來的那一個(gè)。
- prototype
修改spring-ioc.xml:
Junit測(cè)試結(jié)果變成:
上圖可見,聲明了prototype作用域,獲取多次實(shí)例,每一次的hashcode都不同。
余下三個(gè),由于涉及到web,在此就不做測(cè)驗(yàn)了。相信工作中,會(huì)有很多機(jī)會(huì)去嘗試。
Bean的生命周期
- 生命周期
- 定義:在xml中定義<bean></bean>內(nèi)容
- 初始化:IoC容器啟動(dòng)(context.start();)時(shí)生成bean的實(shí)例
- 使用:在測(cè)試或者開發(fā)時(shí)從Ioc容器中取出bean的實(shí)例調(diào)用它的方法
- 銷毀:在IoC容器銷毀(context.destroy();)的時(shí)候,銷毀它創(chuàng)建的所有實(shí)例。
生命周期 —— 初始化
- 實(shí)現(xiàn)org.springframework.beans.factory.InitializingBean接口,重寫afterPropertiesSet方法
- 配置init-method
以上兩種方法,可以在Ioc容器初始化實(shí)例時(shí)執(zhí)行一些實(shí)例內(nèi)部的初始化工作。
下面分別舉例:
為了測(cè)試IoC容器啟動(dòng),我加了一個(gè)空的測(cè)試方法。
執(zhí)行后
第一個(gè)【實(shí)現(xiàn)org.springframework.beans.factory.InitializingBean接口,重寫afterPropertiesSet方法】測(cè)試初始化成功。
第二個(gè)【配置init-method】測(cè)試初始化成功。
**生命周期 —— 銷毀 **
- 實(shí)現(xiàn)org.springframework.beans.factory.DisposableBean接口,重寫destroy方法
- 配置destroy-method
這兩個(gè)銷毀方式和初始化測(cè)試方式是一樣的,我就不做測(cè)試了。
還可以配置全局默認(rèn)初始化、銷毀方法,就是為當(dāng)前IoC容器中所有的bean增加初始化和銷毀時(shí)執(zhí)行的方法
既然實(shí)現(xiàn)初始化和銷毀有三種不同的方法,那么哪種優(yōu)先級(jí)最高呢?下面來測(cè)試。
package test4;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
/**
* Created by amber on 2017/5/27.
*/
public class TestInitial3 implements InitializingBean,DisposableBean {
public void initInBean() {
System.out.println("Bean內(nèi)自定義init——start——" + hashCode());
}
public void destroyInBean(){
System.out.println("Bean內(nèi)自定義destroy——stop——" + hashCode());
}
public void defaultInit() {
System.out.println("IoC全局默認(rèn)defaultInit——start——" + hashCode());
}
public void defaultDestroy(){
System.out.println("IoC全局默認(rèn)defaultDestroy——stop——" + hashCode());
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("重寫接口afterPropertiesSet——start——" + hashCode());
}
@Override
public void destroy() throws Exception {
System.out.println("重寫接口DisposableBean——stop" + hashCode());
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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"
default-init-method="defaultInit" default-destroy-method="defaultDestroy">
<bean id="testInitial3" class="test4.TestInitial3" init-method="initInBean" destroy-method="destroyInBean"></bean>
</beans>
測(cè)試后結(jié)果:
發(fā)現(xiàn),三種初始化和銷毀方法同時(shí)執(zhí)行時(shí),全局默認(rèn)的是不執(zhí)行的。其次,最先執(zhí)行的是接口。然后是bean內(nèi)自定義的。
也就是三個(gè)優(yōu)先級(jí)為:接口>Bean中配置>全局默認(rèn)
其中全局默認(rèn)即使配置了,類里面不寫也不會(huì)報(bào)錯(cuò),其他兩個(gè),則會(huì)報(bào)錯(cuò)。
Aware
- Spring中提供了一些以Aware結(jié)尾的接口,實(shí)現(xiàn)了Aware接口的bean在被初始化之后,可以獲取相應(yīng)資源
- 通過實(shí)現(xiàn)Aware接口,可以對(duì)Spring相應(yīng)資源進(jìn)行操作(一定要慎重)
- 為對(duì)Spring進(jìn)行簡(jiǎn)單的擴(kuò)展,提供了方便的接口
Aware相關(guān)常用接口
- ApplicationContextAware:實(shí)現(xiàn)了此接口的bean,可以獲取到當(dāng)前ApplicationContext的信息。這個(gè)類就可以方便獲得ApplicationContext中的所有bean。換句話說,就是這個(gè)類可以直接獲取spring配置文件中,所有有引用到的bean對(duì)象。
- BeanNameAware:實(shí)現(xiàn)了此接口的bean,可以獲取到自己在IoC中的beanId。
以上接口,都是在bean初始化時(shí)候調(diào)用。
下面測(cè)試接口功能是否如上所述:
package test5;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* Created by amber on 2017/5/27.
*/
public class TestAware implements ApplicationContextAware, BeanNameAware {
private String beanName;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("setApplicationContext執(zhí)行時(shí)間戳:" + System.currentTimeMillis());
System.out.println("獲取當(dāng)前bean的hashCode:" + applicationContext.getBean(beanName).hashCode());
}
@Override
public void setBeanName(String name) {
beanName = name;
System.out.println("setBeanName執(zhí)行時(shí)間戳:" + System.currentTimeMillis());
}
}
IoC容器中
<bean id="testAware" class="test5.TestAware"></bean>
單元測(cè)試中
@Test
public void testAware(){
System.out.println("單元測(cè)試獲取bean的hashCode:"+getBean("testAware").hashCode());
}
結(jié)果如下。
為什么我要加時(shí)間戳呢,主要是為了看哪個(gè)回調(diào)會(huì)先執(zhí)行,經(jīng)過多次測(cè)試發(fā)現(xiàn)BeanNameAware的setBeanName(String name)會(huì)先執(zhí)行。
Bean的自動(dòng)裝配(AutoWiring)
為什么需要自動(dòng)裝配?
其實(shí)目的很簡(jiǎn)單,是偷懶。自動(dòng)裝配可以不用讓我們?cè)贗oC容器的Bean中聲明這些東西啦:
<property name="testDao" ref="testDao"/>
<constructor-arg name="testDao" ref="testDao"/>
關(guān)于Spring自動(dòng)裝配可以參考:spring的自動(dòng)裝配
本次講設(shè)置在<beans>根節(jié)點(diǎn)的全局:default-autowire
- No:(默認(rèn))不自動(dòng)裝配。Bean的引用必須用ref元素定義。對(duì)于較大的部署不建議改變默認(rèn)設(shè)置,因?yàn)槊鞔_指定協(xié)作者能更好控制和維護(hù)系統(tǒng)。 在某種程度上,它記錄了系統(tǒng)的結(jié)構(gòu)。
- byName:通過屬性名稱自動(dòng)裝配。Spring會(huì)尋找相同名稱的bean并將其與屬性自動(dòng)裝配。譬如,如果bean的定義設(shè)置了根據(jù)名稱自動(dòng)裝配, 并且包含了一個(gè)master 屬性(換句話說,它有setMaster(..)方法),Spring會(huì)尋找名為master的bean的定義,并用它來裝配屬性
- byType:如果容器中存在一個(gè)與指定屬性類型相同的bean,那么將與該屬性自動(dòng)裝配。如果存在多個(gè)該類型的bean,將會(huì)拋出異常,并指出不能使用byType自動(dòng)裝配這個(gè)bean。如果沒有找到相同類型的,什么也不會(huì)發(fā)生。屬性不會(huì)被設(shè)置。
- Constructor:和byType類似,不同之處在于它應(yīng)用于構(gòu)造器參數(shù)。如果在容器中沒有找到與構(gòu)造器參數(shù)類型一致的bean,就會(huì)拋出異常。
在測(cè)試之前呢,先把結(jié)構(gòu)拋出來:
自動(dòng)裝配之byName:要保證beanId和調(diào)用它的類屬性名稱一致。
自動(dòng)裝配之byType:將IoC容器中AutoWiringDao的id去掉,只留下class。
自動(dòng)裝配之constructor:
Resource
- 針對(duì)于資源文件的統(tǒng)一接口
- Resources
- UrlResource:URL對(duì)應(yīng)的資源,根據(jù)一個(gè)Url地址即可構(gòu)建
- ClassPathResource:獲取類路徑下的資源文件
- FieSystemResource:獲取文件系統(tǒng)里面的資源
- ServletContextResource:ServletContext封裝的資源,用于訪問ServletContext環(huán)境下的資源
- InputStreamResource:針對(duì)于輸入流封裝的資源
- ByteArrayResource:針對(duì)于字節(jié)數(shù)封裝的資源
Resource Loader
- ResourceLoader 接口是用來加載 Resource 對(duì)象的,換句話說,就是當(dāng)一個(gè)對(duì)象需要獲取 Resource 實(shí)例時(shí),可以選擇實(shí)現(xiàn) ResourceLoader 接口。
- spring 里所有的應(yīng)用上下文都是實(shí)現(xiàn)了 ResourceLoader 接口,因此,所有應(yīng)用上下文都可以通過 getResource() 方法獲取 Resource 實(shí)例。
Resource Loader接口詳情:
可以看到,我們需要傳入一個(gè)location進(jìn)去,然后接口會(huì)返回一個(gè)對(duì)應(yīng)Resource給我們。那么location這個(gè)值,我們都可以傳哪樣的呢?
前綴 | 樣例 | 說明 |
---|---|---|
classpath: | classpath:com/myapp/config.xml | 從類路徑加載 |
file: | file:///data/config.xml | 將其作為 URL 對(duì)象,從文件系統(tǒng)加載 |
http: | http://myserver/logo.png | 將其作為 URL 對(duì)象 加載 |
(none) | /data/config.xml | 取決于底層的 ApplicationContext |
下面來測(cè)試:
使用classpath獲取資源
1.首先在resource文件夾下增加一個(gè)config.txt文件。
2.新建一個(gè)TesrResource類,并通過實(shí)現(xiàn)ApplicationContextAware接口獲取到ApplicationContext,然后傳入classpath:位置的地址得到目標(biāo)文件信息。
3.IoC容器注冊(cè)bean
4.測(cè)試
5.結(jié)果
使用file:獲取資源
1.修改TestResource類的resource方法
2.測(cè)試結(jié)果:
使用http:獲取資源
1.修改resource方法
2.測(cè)試結(jié)果
不使用前綴獲取資源
1.修改resource方法
2.測(cè)試結(jié)果
為什么沒有前綴也能獲取到我們想要的文件呢?
- 當(dāng)你在指定應(yīng)用上下文調(diào)用 getResource() 方法時(shí),而指定的位置路徑又沒有包含特定的前綴,spring 會(huì)根據(jù)當(dāng)前應(yīng)用上下文來決定返回哪一種類型 Resource。
- 這個(gè)例子,我們ApplicationContext是通過ClassPathXmlApplicationContext獲取到的,所以返回的是ClassPathResource對(duì)象。類似的,如果是通過實(shí)例 FileSystemXmlApplicationContext 實(shí)例調(diào)用的,返回的是一個(gè) FileSystemResource 對(duì)象;如果是通過 WebApplicationContext 實(shí)例的,返回的是一個(gè) ServletContextResource 對(duì)象…… 如上所說,你就可以在指定的應(yīng)用上下中使用 Resource 實(shí)例來加載當(dāng)前應(yīng)用上下文的資源。