上一篇:Spring學(xué)習(xí)筆記(三秆吵、IoC)
** Bean常用的配置項**
- Id:在IoC容器中Bean的唯一標(biāo)識
- Class:具體要實例化的類
- Scope:作用域
- Constructor arguments:構(gòu)造器參數(shù)
- properties:屬性
- Autowiring mode:自動裝配模式
- lazy-initialization mode:懶加載模式
- initialization/destruction method:初始化/銷毀的方法
Bean的作用域
- singleton:單例遏插,指一個Bean容器中只存在一份(默認(rèn))
- prototype:每次請求(每次使用)創(chuàng)建新的實例,destory方法不生效(因為用完會被垃圾回收期回收)
- request:每次http請求創(chuàng)建一個實例且僅在當(dāng)前request內(nèi)有效
- session: 每次http請求創(chuàng)建一個實例且僅在當(dāng)前session內(nèi)有效
- global session:基于portlet的web中有效(portlet定義了global session)渤弛,如果是在web中,同session
這里global session有可能還是不懂甚带,特此解釋:舉個例子她肯,一個大型的系統(tǒng),有多個獨立的模塊組成鹰贵,他們有一個統(tǒng)一的登錄入口晴氨,登錄進(jìn)入后,用戶可以在整個系統(tǒng)內(nèi)操作碉输,不需要在登錄其他模塊時籽前,重新登錄。這就是portlet的global session敷钾。
下面用例子證明:
-
singleton
在TestDao增加接口:
Paste_Image.png
TestDaoImpl實現(xiàn):
Paste_Image.png
spring-ioc.xml:
Paste_Image.png
JUnit測試:
上圖可見枝哄,聲明了singleton作用域,即使獲取多次實例阻荒,也依舊是原來的那一個挠锥。
- prototype
修改spring-ioc.xml:
Junit測試結(jié)果變成:
上圖可見,聲明了prototype作用域侨赡,獲取多次實例蓖租,每一次的hashcode都不同。
余下三個羊壹,由于涉及到web蓖宦,在此就不做測驗了。相信工作中舶掖,會有很多機會去嘗試球昨。
Bean的生命周期
- 生命周期
- 定義:在xml中定義<bean></bean>內(nèi)容
- 初始化:IoC容器啟動(context.start();)時生成bean的實例
- 使用:在測試或者開發(fā)時從Ioc容器中取出bean的實例調(diào)用它的方法
- 銷毀:在IoC容器銷毀(context.destroy();)的時候尔店,銷毀它創(chuàng)建的所有實例眨攘。
生命周期 —— 初始化
- 實現(xiàn)org.springframework.beans.factory.InitializingBean接口主慰,重寫afterPropertiesSet方法
- 配置init-method
以上兩種方法,可以在Ioc容器初始化實例時執(zhí)行一些實例內(nèi)部的初始化工作鲫售。
下面分別舉例:
為了測試IoC容器啟動共螺,我加了一個空的測試方法。
執(zhí)行后
第一個【實現(xiàn)org.springframework.beans.factory.InitializingBean接口情竹,重寫afterPropertiesSet方法】測試初始化成功藐不。
第二個【配置init-method】測試初始化成功。
**生命周期 —— 銷毀 **
- 實現(xiàn)org.springframework.beans.factory.DisposableBean接口秦效,重寫destroy方法
- 配置destroy-method
這兩個銷毀方式和初始化測試方式是一樣的雏蛮,我就不做測試了。
還可以配置全局默認(rèn)初始化阱州、銷毀方法挑秉,就是為當(dāng)前IoC容器中所有的bean增加初始化和銷毀時執(zhí)行的方法
既然實現(xiàn)初始化和銷毀有三種不同的方法,那么哪種優(yōu)先級最高呢苔货?下面來測試犀概。
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>
測試后結(jié)果:
發(fā)現(xiàn),三種初始化和銷毀方法同時執(zhí)行時夜惭,全局默認(rèn)的是不執(zhí)行的姻灶。其次,最先執(zhí)行的是接口诈茧。然后是bean內(nèi)自定義的产喉。
也就是三個優(yōu)先級為:接口>Bean中配置>全局默認(rèn)
其中全局默認(rèn)即使配置了,類里面不寫也不會報錯若皱,其他兩個疾瓮,則會報錯。
Aware
- Spring中提供了一些以Aware結(jié)尾的接口疯趟,實現(xiàn)了Aware接口的bean在被初始化之后叭首,可以獲取相應(yīng)資源
- 通過實現(xiàn)Aware接口,可以對Spring相應(yīng)資源進(jìn)行操作(一定要慎重)
- 為對Spring進(jìn)行簡單的擴展互广,提供了方便的接口
Aware相關(guān)常用接口
- ApplicationContextAware:實現(xiàn)了此接口的bean敛腌,可以獲取到當(dāng)前ApplicationContext的信息。這個類就可以方便獲得ApplicationContext中的所有bean惫皱。換句話說像樊,就是這個類可以直接獲取spring配置文件中,所有有引用到的bean對象旅敷。
- BeanNameAware:實現(xiàn)了此接口的bean生棍,可以獲取到自己在IoC中的beanId。
以上接口媳谁,都是在bean初始化時候調(diào)用涂滴。
下面測試接口功能是否如上所述:
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í)行時間戳:" + 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í)行時間戳:" + System.currentTimeMillis());
}
}
IoC容器中
<bean id="testAware" class="test5.TestAware"></bean>
單元測試中
@Test
public void testAware(){
System.out.println("單元測試獲取bean的hashCode:"+getBean("testAware").hashCode());
}
結(jié)果如下友酱。
為什么我要加時間戳呢,主要是為了看哪個回調(diào)會先執(zhí)行柔纵,經(jīng)過多次測試發(fā)現(xiàn)BeanNameAware的setBeanName(String name)會先執(zhí)行缔杉。
Bean的自動裝配(AutoWiring)
為什么需要自動裝配?
其實目的很簡單搁料,是偷懶或详。自動裝配可以不用讓我們在IoC容器的Bean中聲明這些東西啦:
<property name="testDao" ref="testDao"/>
<constructor-arg name="testDao" ref="testDao"/>
關(guān)于Spring自動裝配可以參考:spring的自動裝配
本次講設(shè)置在<beans>根節(jié)點的全局:default-autowire
- No:(默認(rèn))不自動裝配。Bean的引用必須用ref元素定義郭计。對于較大的部署不建議改變默認(rèn)設(shè)置霸琴,因為明確指定協(xié)作者能更好控制和維護(hù)系統(tǒng)。 在某種程度上昭伸,它記錄了系統(tǒng)的結(jié)構(gòu)沈贝。
- byName:通過屬性名稱自動裝配。Spring會尋找相同名稱的bean并將其與屬性自動裝配勋乾。譬如宋下,如果bean的定義設(shè)置了根據(jù)名稱自動裝配, 并且包含了一個master 屬性(換句話說辑莫,它有setMaster(..)方法)学歧,Spring會尋找名為master的bean的定義,并用它來裝配屬性
- byType:如果容器中存在一個與指定屬性類型相同的bean各吨,那么將與該屬性自動裝配枝笨。如果存在多個該類型的bean,將會拋出異常揭蜒,并指出不能使用byType自動裝配這個bean横浑。如果沒有找到相同類型的,什么也不會發(fā)生屉更。屬性不會被設(shè)置徙融。
- Constructor:和byType類似,不同之處在于它應(yīng)用于構(gòu)造器參數(shù)瑰谜。如果在容器中沒有找到與構(gòu)造器參數(shù)類型一致的bean欺冀,就會拋出異常。
在測試之前呢萨脑,先把結(jié)構(gòu)拋出來:
自動裝配之byName:要保證beanId和調(diào)用它的類屬性名稱一致隐轩。
自動裝配之byType:將IoC容器中AutoWiringDao的id去掉,只留下class渤早。
自動裝配之constructor:
Resource
- 針對于資源文件的統(tǒng)一接口
- Resources
- UrlResource:URL對應(yīng)的資源职车,根據(jù)一個Url地址即可構(gòu)建
- ClassPathResource:獲取類路徑下的資源文件
- FieSystemResource:獲取文件系統(tǒng)里面的資源
- ServletContextResource:ServletContext封裝的資源,用于訪問ServletContext環(huán)境下的資源
- InputStreamResource:針對于輸入流封裝的資源
- ByteArrayResource:針對于字節(jié)數(shù)封裝的資源
Resource Loader
- ResourceLoader 接口是用來加載 Resource 對象的,換句話說悴灵,就是當(dāng)一個對象需要獲取 Resource 實例時军援,可以選擇實現(xiàn) ResourceLoader 接口。
- spring 里所有的應(yīng)用上下文都是實現(xiàn)了 ResourceLoader 接口称勋,因此,所有應(yīng)用上下文都可以通過 getResource() 方法獲取 Resource 實例涯竟。
Resource Loader接口詳情:
可以看到赡鲜,我們需要傳入一個location進(jìn)去,然后接口會返回一個對應(yīng)Resource給我們庐船。那么location這個值银酬,我們都可以傳哪樣的呢?
前綴 | 樣例 | 說明 |
---|---|---|
classpath: | classpath:com/myapp/config.xml | 從類路徑加載 |
file: | file:///data/config.xml | 將其作為 URL 對象筐钟,從文件系統(tǒng)加載 |
http: | http://myserver/logo.png | 將其作為 URL 對象 加載 |
(none) | /data/config.xml | 取決于底層的 ApplicationContext |
下面來測試:
使用classpath獲取資源
1.首先在resource文件夾下增加一個config.txt文件揩瞪。
2.新建一個TesrResource類,并通過實現(xiàn)ApplicationContextAware接口獲取到ApplicationContext篓冲,然后傳入classpath:位置的地址得到目標(biāo)文件信息李破。
3.IoC容器注冊bean
4.測試
5.結(jié)果
使用file:獲取資源
1.修改TestResource類的resource方法
2.測試結(jié)果:
使用http:獲取資源
1.修改resource方法
2.測試結(jié)果
不使用前綴獲取資源
1.修改resource方法
2.測試結(jié)果
為什么沒有前綴也能獲取到我們想要的文件呢?
- 當(dāng)你在指定應(yīng)用上下文調(diào)用 getResource() 方法時壹将,而指定的位置路徑又沒有包含特定的前綴嗤攻,spring 會根據(jù)當(dāng)前應(yīng)用上下文來決定返回哪一種類型 Resource。
- 這個例子诽俯,我們ApplicationContext是通過ClassPathXmlApplicationContext獲取到的妇菱,所以返回的是ClassPathResource對象。類似的暴区,如果是通過實例 FileSystemXmlApplicationContext 實例調(diào)用的闯团,返回的是一個 FileSystemResource 對象;如果是通過 WebApplicationContext 實例的仙粱,返回的是一個 ServletContextResource 對象…… 如上所說房交,你就可以在指定的應(yīng)用上下中使用 Resource 實例來加載當(dāng)前應(yīng)用上下文的資源。