一、概述
今天來學(xué)習(xí)一下Spring的核心配置文件applicationContext.xml观谦,主要來看一下其中經(jīng)常使用的標(biāo)簽常挚,當(dāng)然由于Spring早就支持注解的方式性含,所以我們以后的側(cè)重點(diǎn)將也是注解方式,但對(duì)于applicationContext.xml中的各項(xiàng)配置厘线,其實(shí)與注解是很像的识腿,等我們理解了xml配置之后,很容易就理解注解配置了造壮。
注:學(xué)習(xí)的Spring版本是4.3.14渡讼。
二骂束、各種標(biāo)簽
1. beans標(biāo)簽
Spring配置文件的根元素是beans
節(jié)點(diǎn),在該節(jié)點(diǎn)內(nèi)成箫,我們可以配置Spring內(nèi)置的各種命名空間以及bean默認(rèn)的幾項(xiàng)配置展箱,通過配置各種命名空間,然后使用各命名空間的元素來完成對(duì)Spring的配置蹬昌。
<?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">
</beans>
前面我們說過命名空間混驰,這里就不多說了。Spring內(nèi)置了大約10種左右的命名空間供我們選擇配置:
命名空間 | 用途 |
---|---|
aop | 為聲明切面及將@AspectJ注解的類代理為Spring切面提供了配置元素 |
beans | 聲明及配置Bean皂贩,是Spring最核心也是最原始的命名空間 |
context | 為配置Spring應(yīng)用上下文提供了配置元素栖榨,包括自動(dòng)檢測(cè),自動(dòng)裝配bean等 |
jee | 提供了于Java EE API的集成明刷,比如JNDI與EJB |
jms | Java消息相關(guān)的配置 |
lang | 支持配置由Groovy婴栽、JRuby或BeanShell等腳本實(shí)現(xiàn)的Bean |
mvc | 提供Spring MVC相關(guān)的配置,比如控制器辈末,攔截器愚争,視圖管理器等相關(guān) |
oxm | 支持Spring的對(duì)象到XML的映射配置 |
tx | 聲明式的事務(wù)配置 |
util | 提供各種各樣的工具類元素配置,包括把集合配置為Bean挤聘,支持屬性占位符元素 |
當(dāng)然准脂,Spring的命名空間不止如此,只是展示了我們常用的而已檬洞,其他的等我們用到的時(shí)候再了解不遲。而我們?nèi)绻枰硞€(gè)命名空間下的元素時(shí)沟饥,引入相應(yīng)的命名空間即可添怔。比如說我們使用到了context命名空間:
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:property-placeholder ignore-resource-not-found="false" ignore-unresolvable="false"/>
</beans>
接下來,我們來看下beans標(biāo)簽中除了命名空間外的其他默認(rèn)元素贤旷。
1.1 default-init-method
元素
如果在上下文中定義的很多bean都有相同名字的初始化方法广料,我們沒有必要為每一個(gè)bean聲明init-method
屬性,這時(shí)候我們就可以使用beans元素的default-init-method
屬性幼驶。該屬性為應(yīng)用上下文中所有的bean設(shè)置了共同的初始化方法艾杏。
1.2 default-destory-method
元素
這個(gè)元素于上述default-init-method
類似,該屬性是用于方法的銷毀盅藻。
1.3 default-lazy-init
元素
用于延遲加載购桑,我們都知道,一般情況下氏淑,Spring在啟動(dòng)的時(shí)候會(huì)初始化所有相關(guān)的bean勃蜘,這樣就會(huì)出現(xiàn)啟動(dòng)的時(shí)候會(huì)特別慢的問題。這時(shí)候可以配置該屬性設(shè)置為延遲加載假残,用到的時(shí)候再去加載缭贡。當(dāng)然如果該配置文件配置了bean標(biāo)簽或者引入了其他的配置文件,以具體的bean中的配置或其他配置文件的配置優(yōu)先;
- true阳惹,設(shè)置為true谍失,則所有相關(guān)的bean的初始化被延遲加載;
- false莹汤,默認(rèn)為false快鱼,即不延遲加載;
- 假如設(shè)置為延遲加載后体啰,記得考慮比如定時(shí)任務(wù)等相關(guān)的操作攒巍,所以是否使用該屬性,按需選擇荒勇;
1.4 default-autowire
元素
Spring中的bean默認(rèn)注入的方式柒莉。一般情況下,當(dāng)我們需要往一個(gè)bean里注入另一個(gè)bean的時(shí)候沽翔,如果沒有配置該屬性兢孝,就需要手動(dòng)通過ref標(biāo)簽引入。我們以一個(gè)簡單的例子來看下:
(1). 兩個(gè)對(duì)象:Student仅偎,Score
public class Student {
private String id;
private String name;
private Score score;
}
public class Score {
private Integer math;
}
(2). bean配置:
<bean id="student" class="entity.Student">
<property name="id" value="id"/>
<property name="name" value="name"/>
</bean>
<bean id="score" class="entity.Score">
<property name="math" value="100"></property>
</bean>
(3). main方法
public static void main(String[] args) {
ApplicationContext ac=new ClassPathXmlApplicationContext("/resource/applicationContext.xml");
Student student= ac.getBean("student"跨蟹, Student.class);
System.out.println(student.getScore().getMath());
}
首先,我們配置default-autowire
屬性橘沥,也就是默認(rèn)不自動(dòng)注入窗轩,這時(shí)候,我們?nèi)绻苯荧@取的話座咆,就獲取不到Score對(duì)象痢艺,提示空指針異常:
Exception in thread "main" java.lang.NullPointerException
at test.Main.main(Main.java:15)
我們配置default-autowire
屬性為byName
之后,再看下結(jié)果:
100
這時(shí)候打印成功介陶,也就是說堤舒,如果我們不配置該屬性的話,需要手動(dòng)使用ref標(biāo)簽來引用:
<bean id="student" class="entity.Student">
<property name="id" value="id"/>
<property name="name" value="name"/>
<property name="score" ref="score"/>
</bean>
當(dāng)然哺呜,如果我們的對(duì)象是單個(gè)對(duì)象的話舌缤,那無論配置是否配置,都沒什么影響某残。
下面我們來看下該屬性的幾個(gè)可選值:
- no国撵,不使用自動(dòng)注入,所以該Bean對(duì)象中其他Bean的引用必須通過ref標(biāo)簽來引用玻墅;
- byName卸留,把與beanA的屬性具有相同名字的beanB自動(dòng)注入到beanA的對(duì)應(yīng)屬性中,如果沒有椭豫,則不注入耻瑟。這里的相同指的是beanB配置的id或name其中一個(gè)相同即可旨指;
- byType,與byName相對(duì)喳整,這里指的是相同類型谆构,也就是相同的class,這種情況下框都,由于根據(jù)類型來注入搬素,所以beanB只能有一個(gè)存在,否則會(huì)編譯不通過魏保;
- constructor熬尺,與byType類似,通過構(gòu)造方法的參數(shù)來匹配谓罗,如果沒有構(gòu)造方法粱哼,或沒有與構(gòu)造方法參數(shù)類型一致的bean,則會(huì)提示異常檩咱;
這里判斷是否存在類型一致先通過byType判斷揭措,如果只有一個(gè)類型的bean存在,則成功刻蚯;如果有多個(gè)類型一致的绊含,再根據(jù)byName,如果只有一個(gè)name相同的炊汹,則成功躬充,否則失敗,提示異常讨便;
-
default充甚,默認(rèn)配置,由上級(jí)標(biāo)簽<beans>的
default-autowire
屬性確定使用哪種裝配方式器钟,該屬性一般配置于具體的bean標(biāo)簽中,而配置在beans標(biāo)簽中和配置為no
類似妙蔗。
注意:在以前版本中傲霸,
default-autowire
還支持一個(gè)可選值:autodetect
,不過在Spring4之后已經(jīng)廢棄掉眉反。
還有兩點(diǎn)簡單說下:
- 和
default-lazy-init
類似昙啄,如果bean中配置了autowire
屬性,則具體bean中的注入類型的優(yōu)先級(jí)將高于beans里的配置寸五;- 當(dāng)然梳凛,配置了該屬性后可能會(huì)導(dǎo)致我們對(duì)其中的細(xì)節(jié)不夠了解,也會(huì)弱化對(duì)象間的關(guān)系梳杏,出現(xiàn)問題的時(shí)候可能不太好定位韧拒,所以我們可以根據(jù)需要選擇配置或不配置
default-autowire
淹接;
1.5 default-autowire-candidates
元素
自動(dòng)注入bean的候選者,比如說叛溢,default-autowire
中我們使用byType
的時(shí)候通常只能有一個(gè)類型為ClassA的bean塑悼。但是我們想在配置文件中有多個(gè)類型為ClassA的bean,就可以通過配置default-autowire-candidates
來作為自動(dòng)注入bean的候選者楷掉;
<beans ...
default-autowire-candidates="*score" default-autowire="byType">
<bean id="student" class="entity.Student">
<property name="id" value="id"/>
<property name="name" value="name"/>
</bean>
<bean id="score2" class="entity.Score" >
<property name="math" value="100"></property>
</bean>
<bean id="score" class="entity.Score" >
<property name="math" value="200"></property>
</bean>
</beans>
在上面這個(gè)小例子中厢蒜,我們通過配置該屬性為*socre
,讓容器在進(jìn)行自動(dòng)注入的時(shí)候選擇name或id以score
結(jié)尾的bean來進(jìn)行注入烹植。
default-autowire-candidates
的值可以是某個(gè)bean的id斑鸦,也可以是匹配字符串,比如*score
草雕,則會(huì)將以score結(jié)尾的作為候選注入的bean巷屿,也可以指定多個(gè)字符串,通過逗號(hào)進(jìn)行分割促绵;- A default bean name pattern for identifying autowire candidates:
e.g. "*Service", "data*", "*Service*", "data*Service".
Also accepts a comma-separated list of patterns: e.g. "*Service,*Dao".- 有關(guān)候選bean的細(xì)節(jié)攒庵,我們可以查看官方文檔中
bean
的屬性autowire-candidate
的文檔來更深一步學(xué)習(xí)。
1.6 default-merge
元素
這個(gè)屬性用的可能不太多败晴,這是用于集合的繼承相關(guān)的操作浓冒。比如說,如果父類Bean包含list尖坤,map稳懒,set等一些集合元素,那么繼承父類的子類將自動(dòng)繼承父類Bean的集合元素慢味;當(dāng)然场梆,子類Bean可以選擇合并父類的集合元素,也可以選擇替換父類的的集合元素纯路,Spring通過beans標(biāo)簽的default-merge
和bean標(biāo)簽內(nèi)的list標(biāo)簽的merge
來支持這種操作或油;
我們通過簡單的例子來看一下:定義對(duì)象Tutorial:
public class Tutorial {
private String name;
private List topicsList = new ArrayList<>();
}
配置兩個(gè)bean,先不配置default-merge
屬性:
<bean id="parent" class="entity.Tutorial">
<property name="name" value="Java parent"/>
<property name="topicsList">
<list>
<value>Java Core</value>
<value>Java Concurrency</value>
</list>
</property>
</bean>
<bean id="children" parent="parent">
<property name="name" value="Java children"/>
<property name="topicsList">
<list>
<value>EJB</value>
<value>Servlet</value>
</list>
</property>
</bean>
指定parent
的Bean集合數(shù)據(jù)為[Java Code, Java Concurrency]
驰唬,而children
中的集合數(shù)據(jù)為[EJB, Servlet]
顶岸,這時(shí)候我們測(cè)試下chidren是否合并了父類的集合元素:
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/resource/applicationContext.xml");
try {
Tutorial parent = context.getBean("parent", Tutorial.class);
Tutorial children = context.getBean("children", Tutorial.class);
// 獲取子類數(shù)組
List childrenList = children.getTopicsList();
// 獲取父類數(shù)組
List parentList = parent.getTopicsList();
System.out.println(children.getName() + " contain " + parent.getName() + "? " +
childrenList.containsAll(parentList));
System.out.println(children.getName() + ": " + childrenList);
} finally {
context.close();
}
}
打印結(jié)果為:
Java children contain Java parent? false
Java children: [EJB, Servlet]
可以看到,children并沒有合并父類的集合元素叫编,只包含本身的元素辖佣。我們?cè)O(shè)置default-merge=true
,再來看一下結(jié)果:
Java children contain Java parent? true
Java children: [Java Core, Java Concurrency, EJB, Servlet]
此時(shí)搓逾,我們可以看到卷谈,子類已經(jīng)合并了父類的集合元素了。
從上面這個(gè)簡單的小例子我們大概了解了該標(biāo)簽的用處霞篡,該標(biāo)簽有
true
和false
兩個(gè)可選值世蔗,默認(rèn)情況下是false
端逼,就是不合并父類的集合元素。同理凸郑,如果 list 標(biāo)簽配置了merge屬性裳食,則會(huì)覆蓋掉beans標(biāo)簽里的該項(xiàng)配置。如果要查看更多實(shí)例芙沥,可以參考:Spring Collection Merging Example
2. bean標(biāo)簽
bean元素是Spring中最基本的配置單元诲祸,用來管理對(duì)象。通過該元素而昨,Spring將創(chuàng)建一個(gè)對(duì)象救氯,并在容器加載的時(shí)候?qū)嵗搶?duì)象。我們來看一下它的一些屬性:
2.1 id
屬性
bean的唯一標(biāo)識(shí)歌憨,命名格式必須符合XML中ID屬性的命名規(guī)范着憨,不能有特殊字符;
在Spring3.1版本之前务嫡,該id屬性被定義為
xsd:ID
甲抖,通過XML的id規(guī)范限制了它的唯一性,而在Spring3.1中心铃,該id屬性被定義為xsd:string
類型准谚,盡管不再使用XML解析器,但id的唯一性仍由容器強(qiáng)制執(zhí)行去扣。
2.1 name
屬性
bean的名稱柱衔,命名可以比較隨意,可以有特殊字符愉棱,并且一個(gè)bean可以有多個(gè)名稱:name="name1,name2,name3"
算墨,用逗號(hào)或分號(hào)或空格隔開(當(dāng)然沐旨,在同一個(gè)name配置中存淫,只能有一種符號(hào)分割谜喊,不能同時(shí)有多種)。name可以算作是 id 的別名朋其,我們?cè)讷@取bean的時(shí)候王浴,可以通過id,也可以通過name來獲取令宿。在沒有id的情況下叼耙,則name的第一個(gè)名稱默認(rèn)為id腕窥;
- 同一個(gè)Spring配置文件中粒没,id,name是不能重復(fù)的簇爆;但如果一個(gè)Spring從多個(gè)配置文件中加載癞松,則多個(gè)配置文件中是允許id或者name重復(fù)的爽撒,但后面加載的bean會(huì)覆蓋掉前面加載的bean;
- 如果一個(gè)bean標(biāo)簽未指定id响蓉,name屬性硕勿,則Spring會(huì)給一個(gè)默認(rèn)的id,值為類的全名枫甲,比如類的路徑是
com.company.Score
源武,則默認(rèn)的id值是com.company.Score
;- 如果多個(gè)同類型的bean未指定id想幻,name屬性粱栖,則Spring會(huì)按照其出現(xiàn)的次序,分別給其指定 id 為
類全名#1
脏毯,類全名#2
闹究;
舉個(gè)小例子看一下:
<bean class="entity.Score">
<property name="math" value="100"/>
</bean>
<bean class="entity.Score">
<property name="math" value="200"/>
</bean>
<bean class="entity.Score">
<property name="math" value="300"/>
</bean>
獲取bean的方式:
Score score = ac.getBean("entity.Score", Score.class);
Score score1 = ac.getBean("entity.Score#1", Score.class);
Score score2 = ac.getBean("entity.Score#2", Score.class);
System.out.println(score.getMath());
System.out.println(score1.getMath());
System.out.println(score2.getMath());
打印結(jié)果:
100
200
300
id和name這段解釋可以參考官網(wǎng):https://docs.spring.io/spring/docs/4.3.14.RELEASE/spring-framework-reference/html/beans.html#beans-beanname
2.3 class
屬性
顧名思義,該屬性就是當(dāng)前對(duì)象所引用的類的全路徑食店。
<bean name="student2,student3,student4" class="entity.Student"/>
2.4 autowire
屬性
在上面beans標(biāo)簽的學(xué)習(xí)中渣淤,我們已經(jīng)了解過default-autowire
屬性,這兩個(gè)屬性是一樣的吉嫩,用于注入配置价认,只不過一個(gè)是全局默認(rèn)的,一個(gè)是針對(duì)某一個(gè)bean的率挣。在bean中配置完autowire
屬性后刻伊,該bean的注入方式會(huì)覆蓋掉beans標(biāo)簽所配置的默認(rèn)注入方式。該屬性的配置可選值和default-autowire
一樣椒功,就不多說了捶箱。
2.5 autowire-candidate
屬性
??同樣,該屬性和beans標(biāo)簽中的default-autowire-candidates
元素類似动漾,意思就注入bean的候選者丁屎。再多說一句,前面我們說到配置有autowire屬性的bean旱眯,Spring在實(shí)例化某個(gè)bean的時(shí)候會(huì)在容器中查找匹配的bean晨川,然后通過autowire的配置對(duì)bean進(jìn)行屬性注入,這些被查找的bean我們稱為候選bean删豺;
??該屬性的配置有三個(gè)值:true
共虑,false
,default
呀页。配置為false
的時(shí)候意味著將該bean從候選bean中排除掉妈拌,也就是注入的時(shí)候不注入該bean;
另外再說兩點(diǎn):
- 該屬性只影響到基于類型的注入蓬蝶,而不會(huì)影響到名稱的引用尘分。也就是如果是基于name的autowire猜惋,該屬性將不會(huì)有影響。
- 同樣培愁,bean標(biāo)簽中的
autowire-candidate
屬性的值將優(yōu)先于beans標(biāo)簽的default-autowire-candidates
配置著摔。我們以一個(gè)例子來說一下:
<beans ...
default-autowire="byType" default-autowire-candidates="*score1">
<bean id="score" class="entity.Score" autowire-candidate="true">
<property name="math" value="100"/>
</bean>
<bean class="entity.Score" >
<property name="math" value="200"/>
</bean>
<bean id="student" class="entity.Student" >
<property name="id" value="id"/>
<property name="name" value="name"/>
</bean>
</beans>
通過beans標(biāo)簽的default-autowire-candidates
屬性設(shè)置同類型的候選bean是id以socre1
結(jié)尾的bean,而我們的bean的id是score
定续,但我們配置了該bean的autowire-candidate
為true谍咆,由于bean的優(yōu)先級(jí)高,所以能注入成功私股。
2.6 destroy-method
屬性和init-method
屬性
同樣卧波,在上文beans中,我們已經(jīng)介紹過default-init-method
了庇茫,這兩個(gè)方法用于該bean的初始化方法和銷毀方法港粱。也就是,當(dāng)Spring容器加載該bean的時(shí)候旦签,會(huì)調(diào)用init-method
執(zhí)行初始化操作查坪,然后當(dāng)Spring容器銷毀該bean的時(shí)候(比如調(diào)用close方法),會(huì)調(diào)用destroy-method
來執(zhí)行銷毀操作宁炫。
public static void main(String[] args) {
ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("/resource/applicationContext.xml");
Student student= ac.getBean("student", Student.class);
System.out.println(student.getScore().getMath());
ac.close();
}
這里還需要說下偿曙,
destroy-method
只有在bean的scope
配置為singleton
,也就是單例模式下才會(huì)生效羔巢,原因等我們接下來學(xué)習(xí)到scope
的時(shí)候再細(xì)說望忆。
2.7 lazy-init
元素
??前文beans里說過,為了避免Spring容器啟動(dòng)的時(shí)候加載所有的bean竿秆,導(dǎo)致啟動(dòng)很慢的情況启摄,所以beans標(biāo)簽提供了default-lazy-init
標(biāo)簽來設(shè)置是否開啟懶加載模式。
??同樣幽钢,bean標(biāo)簽里的lazy-init
元素也是用于對(duì)單個(gè)bean判斷歉备,是否需要開啟懶加載機(jī)制,這樣如果開啟的話匪燕,那該bean的加載將不再是容器啟動(dòng)的時(shí)候進(jìn)行調(diào)用蕾羊,而是在第一次進(jìn)行g(shù)etBean的時(shí)候才進(jìn)行加載。
并且帽驯,該元素也是有
true
和false
可選龟再,默認(rèn)情況下是false
,即不開啟懶加載機(jī)制尼变。而且bean里lazy-init
的優(yōu)先級(jí)也是高于最外層beans標(biāo)簽中default-lazy-init
元素的利凑。
我們還是用上文的Student對(duì)象,添加一個(gè)默認(rèn)構(gòu)造方法:
public class Student {
private String id;
private String name;
private Score score;
public Student() {
System.out.println("test lazy init by construct");
}
}
然后先不設(shè)置lazy-init
元素,即采用默認(rèn)值:
<bean id="student" class="entity.Student" >
<property name="id" value="id"/>
<property name="name" value="name"/>
</bean>
然后測(cè)試下:
ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("/resource/applicationContext.xml");
可以看到截碴,我們的構(gòu)造方法被調(diào)用了:
test lazy init by construct
也就是說,我們只是啟動(dòng)了容器蛉威,并沒有去直接獲取bean日丹,但構(gòu)造方法還是被執(zhí)行了。
接下來蚯嫌,我們來配置下該屬性為true哲虾,然后再看下結(jié)果:
<bean id="student" class="entity.Student" lazy-init="true">
<property name="id" value="id"/>
<property name="name" value="name"/>
</bean>
再看下打印結(jié)果,可以看到择示,程序并沒有執(zhí)行我們的構(gòu)造方法束凑。然后我們手動(dòng)獲取下bean:
ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("/resource/applicationContext.xml");
Student student= ac.getBean("student", Student.class);
然后再看下打印結(jié)果:
test lazy init by construct
這時(shí)候又執(zhí)行了我們的構(gòu)造方法≌っぃ可以看到汪诉,程序在懶加載的時(shí)候,會(huì)延遲到第一次調(diào)用該bean的時(shí)候才進(jìn)行加載該bean谈秫,這樣對(duì)于程序啟動(dòng)的壓力就會(huì)少許多扒寄。但或許會(huì)有額外的問題,比如說實(shí)例化的bean配置有問題拟烫,而這個(gè)問題也會(huì)延遲到運(yùn)行的時(shí)候才會(huì)發(fā)覺该编。
不過這里再多說一點(diǎn):
- 如果一個(gè)bean1設(shè)置了立即加載,而該bean引用了一個(gè)延遲加載的bean2硕淑,那么bean1在容器啟動(dòng)的時(shí)候被實(shí)例化课竣,而bean2由于也被bean1引用,所以也會(huì)立即實(shí)例化置媳,而這種情況也符合延遲加載的bean在第一次調(diào)用時(shí)才被實(shí)例化的規(guī)則于樟;
- 對(duì)于定時(shí)任務(wù)類似的這種東西,一般是不設(shè)置為延遲加載的拇囊;
2.8 abstract
和parent
標(biāo)簽
??很多時(shí)候隔披,我們?yōu)榱吮苊鈈ean中屬性的重復(fù),會(huì)用到另外兩個(gè)元素abstract
和parent
寂拆。使用標(biāo)簽abstract
修飾的bean被稱為抽象的bean奢米,這種bean不是用于實(shí)例化,而是用于繼承的情況纠永,也就是用于具體的子類繼承的父類bean鬓长。該元素有true
和false
兩個(gè)值,默認(rèn)情況下該值是false尝江,當(dāng)指定為true的時(shí)候涉波,就是抽象bean,無法實(shí)例化。
??該值一般配合bean的另一個(gè)元素parent
來使用啤覆,parent
用于bean中的繼承關(guān)系苍日,使用該元素,可以繼承父類的屬性窗声。這種子類bean的出現(xiàn)相恃,是為了對(duì)父類bean中公共屬性的封裝,避免XML中屬性的重復(fù)笨觅。
- Spring中的抽象bean和Java中的抽象類有相同點(diǎn)拦耐,但也有不同點(diǎn)。比如說都不能直接實(shí)例化见剩,只能通過子類實(shí)例化的時(shí)候杀糯,先實(shí)例化父類,再實(shí)例化子類苍苞,相當(dāng)于間接的實(shí)例化父類固翰;
- 另一點(diǎn)是Spring中的抽象bean甚至不需要映射到某個(gè)類就可以被子類所引用,比如:
<bean id="test1" abstract="true">
<property name="name" value="name"/>
<property name="id" value="id"/>
</bean>
<bean id="children" class="entity.Children" parent="test1">
<property name="test" value="test/>
</bean>
這種情況就是將公共的屬性進(jìn)行統(tǒng)一注入羹呵,避免XML中各種屬性配置的重復(fù)倦挂,但這種情況下,就需要子類必須要有相應(yīng)的屬性担巩。對(duì)例子而言方援,也就是Children這個(gè)類中必須要有name
和id
屬性。
我們分兩部分來看一個(gè)例子涛癌,一是抽象bean無法初始化犯戏,二是子類bean可以繼承父類bean的屬性:
首先定義Parent對(duì)象:
public class Parent {
private String name;
public Parent() {
System.out.println("Parent初始化");
}
}
配置bean:
<bean id="parent" class="entity.Parent" abstract="true">
<property name="name" value="name"/>
</bean>
測(cè)試下:
ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("/resource/applicationContext.xml");
可以看到,程序并未打印我們?cè)赑arent中設(shè)置的構(gòu)造方法拳话,也就是說我們沒有配置懶加載先匪,但Parent對(duì)象并沒有實(shí)例化。
然后我們?cè)賮砜聪聦傩缘睦^承弃衍,再定義子類Children:
public class Children extends Parent{
private String id;
public Children() {
System.out.println("Children初始化");
}
}
配置子類的bean:
<bean id="children" class="entity.Children" parent="parent">
<property name="id" value="id"/>
</bean>
看一下最終打印結(jié)果:
ClassPathXmlApplicationContext ac=new ClassPathXmlApplicationContext("/resource/applicationContext.xml");
Children children= ac.getBean("children", Children.class);
System.out.println(children.getName() + "," + children.getId());
ac.close();
output:
Parent初始化
Children初始化
name,id
可以看到呀非,先初始化了父類,再初始化子類镜盯,然后子類Children繼承了Parent中的name屬性岸裙。如果我們把parent
這項(xiàng)配置給去掉,我這里為了方便不上傳這部分代碼速缆,直接看下打印結(jié)果:
Parent初始化
Children初始化
null,id
很顯然降允,父類Parent中的name屬性并沒有被繼承過來。
最后艺糜,再簡單說下這里:
- 如果父類bean沒有定義class屬性剧董,則子類bean必須定義class屬性幢尚,并且子類中必須擁有父bean中所有屬性;
- 還有一種情況翅楼,如果父bean有class屬性尉剩,而子bean沒有,那么子類的bean就和父bean是完全一樣的bean毅臊,當(dāng)然理茎,子類的bean也就不能注入任何新的屬性。
2.9 depends-on
標(biāo)簽
??如果一個(gè)bean是另一個(gè)bean的依賴項(xiàng)褂微,通常將一個(gè)bean設(shè)置為另一個(gè)bean的屬性,然后在XML中可以通過ref
標(biāo)簽來引用园爷。而有時(shí)宠蚂,bean之間的關(guān)系并沒有那么直接,比如實(shí)例化DAO之前先實(shí)例化DataBase這種情況童社,而這時(shí)候就可以使用depends-on
標(biāo)簽求厕。
??該標(biāo)簽用于指定bean初始化時(shí)的順序,也就是說一個(gè)beanA初始化之前必須先初始化另一個(gè)beanB扰楼。對(duì)于depends-on
而言呀癣,并不需要beanA持有beanB的一個(gè)引用,只是一種強(qiáng)制性的在beanA初始化之前對(duì)beanB進(jìn)行初始化弦赖。
<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />
- 很多情況下项栏,
depends-on
標(biāo)簽可以使用bean的構(gòu)造器注入或setter注入代替;還有一點(diǎn)蹬竖,該bean的類型應(yīng)該是單例的沼沈,因?yàn)閱卫腷ean才會(huì)被Spring進(jìn)行管理;- 若要對(duì)多個(gè)bean進(jìn)行依賴币厕,
depends-on
提供了一個(gè)bean名稱的列表列另,使用分號(hào),逗號(hào)或空格來作為有效的分隔符旦装;
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
<property name="manager" ref="manager" />
</bean>
<bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
depends-on
可以指定依賴bean在具體bean初始化之前先初始化页衙,也可以指定依賴bean在具體bean銷毀之前先銷毀,同樣阴绢,這時(shí)候bean的類型也只能是單例的店乐。
參考官網(wǎng)文件:https://docs.spring.io/spring/docs/4.3.14.RELEASE/spring/beans-factory-dependson
2.10 primary
標(biāo)簽
??我們?cè)谇懊鎸W(xué)習(xí)類型的自動(dòng)注入的時(shí)候,可能會(huì)出現(xiàn)多個(gè)候選者呻袭,然后我們可以通過autowire-candidate
注解可以設(shè)置某個(gè)或多個(gè)bean在候選bean中被排除掉响巢,或者成為候選bean。而現(xiàn)在棒妨,primary
提供了另一種方式踪古。
??primary
元素是說在類型注入的時(shí)候含长,如果多個(gè)bean被自動(dòng)列入候選者之中,那么可以通過使用primary
元素或@primary
注解讓該bean成為優(yōu)先選擇者伏穆,如果候選者之中只有一個(gè)primary
修飾的bean拘泞,那么這個(gè)bean將會(huì)是自動(dòng)注入的bean。
該屬性同樣有
true
和false
兩個(gè)值枕扫,默認(rèn)情況下是false陪腌;
還拿上面的例子來說:
<bean id="score" class="entity.Score" >
<property name="math" value="100"/>
</bean>
<bean id="score2" class="entity.Score" primary="true">
<property name="math" value="200"/>
</bean>
<bean id="student" class="entity.Student">
<property name="id" value="id"/>
</bean>
測(cè)試代碼就不再寫了,獲取Student中的引用Score的math值:
200
可以看到烟瞧,由于score2
的優(yōu)先級(jí)高诗鸭,Student類型中自動(dòng)注入的就是score2類型。
和
@primary
注解實(shí)現(xiàn)類似功能的還有@Qualifier
注解参滴,等我們接下來學(xué)習(xí)到的時(shí)候再來學(xué)習(xí)强岸。
2.11 factory-bean
和factory-methd
標(biāo)簽
??在前面的學(xué)習(xí)中,我們都是根據(jù)配置中的class全名通過反射進(jìn)行實(shí)例化bean砾赔,而factory-bean
和factory-method
標(biāo)簽可以讓我們通過工廠模式的方法來實(shí)例化bean蝌箍。而工廠模式又大致分為兩種,一種是靜態(tài)工廠方法暴心,另一種是實(shí)例工廠方法妓盲。
- 靜態(tài)工廠,是指Factory本身并不需要實(shí)例化专普,而這個(gè)Factory類中提供了一個(gè)靜態(tài)方法來生成bean對(duì)象悯衬;
- 而實(shí)例工廠是說Factory類中的方法不是靜態(tài)的,這也就要求我們先實(shí)例一個(gè)工廠對(duì)象檀夹,然后通過這個(gè)工廠對(duì)象去獲取我們所需要的bean對(duì)象甚亭。
我們先看下靜態(tài)工廠的簡單例子:
public class Student {
private String id;
private String name;
}
public class StudentFactory {
public StudentFactory() {
System.out.println("執(zhí)行StudentFactory的構(gòu)造方法進(jìn)行初始化操作");
}
private static Student getInstance() {
return new Student();
}
}
我們定義了Student和靜態(tài)工廠StudentFactory,再簡單看下配置:
<bean id="student" class="entity.StudentFactory" factory-method="getInstance">
<property name="name" value="name"/>
</bean>
同樣击胜,我們?cè)贉y(cè)試下:
Student student = ac.getBean("student", Student.class);
System.out.println(student.getName());
成功打涌髡:
name
可以看到,程序并沒有執(zhí)行工廠類的默認(rèn)構(gòu)造方法偶摔,也就是沒有初始化工廠類暇唾,而是通過工廠類的靜態(tài)方法完成了我們bean對(duì)象的初始化工作。
接下來我們?cè)俸唵慰聪聦?shí)例工廠:
首先辰斋,將StudentFactory中的static去掉策州,變?yōu)閷?shí)例方法,然后再看下配置:
<bean id="studentFactory" class="entity.StudentFactory"/>
<bean id="student" factory-bean="studentFactory" factory-method="getInstance">
<property name="name" value="name"/>
</bean>
我們先實(shí)例化工廠類宫仗,再依靠工廠類去獲取我們的bean够挂,然后再看下執(zhí)行結(jié)果:
執(zhí)行StudentFactory的構(gòu)造方法進(jìn)行初始化操作
name
可以,看到藕夫,程序先初始化了工廠類孽糖,然后再通過實(shí)例方法實(shí)例化了我們的bean對(duì)象枯冈。
工廠模式還是挺方便的,我們?cè)趯?shí)例化對(duì)象的時(shí)候办悟,可以嘗試使用工廠模式來執(zhí)行我們的初始化操作尘奏。
2.12 scope
標(biāo)簽
scope標(biāo)簽表示的bean的作用域,默認(rèn)情況下我們所創(chuàng)建的bean都是單例的病蛉。Spring支持以下幾種作用域:
- singleton炫加,默認(rèn)作用域,在一個(gè)Spring容器中铺然,一個(gè)bean只會(huì)有一個(gè)實(shí)例俗孝,這意味著所有的bean都是共享的,很適合無狀態(tài)的bean定義魄健;
- prototype赋铝,該作用域與
singleton
相反,每次請(qǐng)求都會(huì)返回新的實(shí)例诀艰,從某種意義上類似于Java的new
操作符柬甥,適合有狀態(tài)的bean定義饮六。但需要注意一點(diǎn)就是Spring對(duì)于該模式的bean其垄,創(chuàng)建完實(shí)例后,就交給對(duì)象本身管理自己的生命周期卤橄,不會(huì)自動(dòng)做一些銷毀的操作绿满。所以由于該模式下生命周期并不是由Spring進(jìn)行管理,所以這種情況下配置的如destory-method
便不會(huì)生效窟扑。所以如果可以的話喇颁,我們可以在合適的適合調(diào)用Spring的銷毀方法,讓Spring來銷毀對(duì)象釋放資源嚎货。- request橘霎,該模式是說每次HTTP請(qǐng)求中,每個(gè)Bean都會(huì)生成一個(gè)實(shí)例殖属,也就是說每次HTTP請(qǐng)求都將會(huì)產(chǎn)生不同的實(shí)例姐叁。和
prototype
類似忆某,但該作用域僅在Web請(qǐng)求中才有效遣铝;- session,該模式是說每次HTTP Session中贞间,每個(gè)bean都會(huì)生成一個(gè)實(shí)例挠唆,同樣处窥,作用域僅在Web請(qǐng)求中有效;
- globalSession玄组,每個(gè)全局的HTTP Session滔驾,每個(gè)bean都會(huì)生成一個(gè)實(shí)例谒麦,該作用域僅在Portlet 上下文中有效,使用較少嵌灰;Portlet規(guī)范是類似于Servlet的一種規(guī)范弄匕,有興趣的童鞋可自行了解下。
- application沽瞭,該模式和單例模式有點(diǎn)像迁匠,但
application
在ServletContext
中是單例的,而singleton
的應(yīng)用范圍是ApplicationContext
驹溃,同樣城丧,僅在Web請(qǐng)求中有效;簡單看下注解及XML配置:
@Component
@Scope("application")
public class BeanClass {
}
//or
@Component
@ApplicationScope
public class BeanClass {
}
<bean id="beanId" class="com.howtodoinjava.BeanClass" scope="application" />
- WebSocket豌鹤,該模式用于HTTP中遠(yuǎn)程主機(jī)間進(jìn)行雙向通信亡哄,同樣,也是僅在Web請(qǐng)求下有效布疙。通常 WebSocket范圍內(nèi)的bean通常是單例的蚊惯,并且比任何單獨(dú)的WebSocket會(huì)話要活的更長。注解和XML如下:
@Component
@Scope("websocket")
public class BeanClass {
}
<bean id="beanId" class="com.howtodoinjava.BeanClass" scope="websocket" />
- 還可以自定義線程的scope灵临,這里就不多說了截型,想查看更多有關(guān)scope的內(nèi)容,可參考官方文檔儒溉,上面講的非常清楚:
https://docs.spring.io/spring/docs/4.3.14.RELEASE/spring-framework-reference/html/beans.html#beans-factory-scopes
還可以參考:Spring Bean Scopes
需要注意下宦焦,因?yàn)閟ingleton適合無狀態(tài)的情況,所以在多線程的時(shí)候顿涣,如果bean被定義為單例模式波闹,可能會(huì)出現(xiàn)線程安全問題,這里需要注意下涛碑。
總結(jié)下:
- 在Spring中精堕,我們使用最多的就是singleton和prototype模式,singleton是一種無狀態(tài)的bean類型蒲障,當(dāng)項(xiàng)目中不存在多線程共享對(duì)象的時(shí)候可以選擇singleton模式歹篓,這樣可以稍微節(jié)約資源。使用prototype的時(shí)候記得注意對(duì)象的銷毀操作晌涕。
- 在Spring配置文件XML中直接配置application,websocket滋捶,會(huì)報(bào)錯(cuò),還沒搞清楚為什么余黎。
3. alias
標(biāo)簽
這個(gè)標(biāo)簽用于為bean提供別名重窟。根據(jù)官方文檔解釋:
- 在bean本身定義中,我們可以通過id和name屬性來為bean提供多個(gè)名稱惧财,這些名稱等價(jià)于相同的bean巡扇,在某種情況下是很有用的扭仁,比如應(yīng)用程序中的每個(gè)組件,使用特定于該組件本身的bean名稱來引用公共的依賴關(guān)系厅翔。
- 但有的時(shí)候這種方式定義的別名并不總是能滿足我們的所有需求乖坠,這時(shí)候就可以通過標(biāo)簽
alias
來實(shí)現(xiàn)另一種別名的定義。比如說在大型系統(tǒng)中刀闷,每個(gè)子系統(tǒng)都有自己的一套配置熊泵,組件A通過subsystemA-dataSource
名稱定義了一個(gè)數(shù)據(jù)源bean,而組件B想通過subsystemB-dataSource
引用這一個(gè)數(shù)據(jù)源甸昏,而主系統(tǒng)也想通過myApp-dataSource
來引用該數(shù)據(jù)源顽分,這種情況下就可以使用alias
標(biāo)簽來完成這種操作。- 這樣一來施蜜,每個(gè)組件及主程序就可以通過一個(gè)唯一的名字來引用同一個(gè)數(shù)據(jù)源卒蘸,而相互間不會(huì)受到干擾。
- 官方文檔地址:https://docs.spring.io/spring/docs/4.3.14.RELEASE/spring-framework-reference/html/beans.html#beans-beanname-alias
使用方式很簡單:
<alias name="subsystemA-dataSource" alias="subsystemB-dataSource"/>
<alias name="subsystemA-dataSource" alias="myApp-dataSource" />
不過需要保證alias
唯一翻默,不能重復(fù)缸沃。
4. description
標(biāo)簽
這個(gè)標(biāo)簽就不多說了,就是對(duì)該配置文件的描述信息修械,不過配置的時(shí)候注意下順序趾牧,一般用于配置的最頂層。
<description>Spring MVC Test</description>
5. import
標(biāo)簽
在我們的Spring配置中祠肥,隨著我們項(xiàng)目的越來越大武氓,配置文件的越來越復(fù)雜梯皿,模塊化則是我們需要考慮的問題了仇箱,而import
標(biāo)簽則是充當(dāng)了這樣的角色,我們可以通過import
標(biāo)簽將多個(gè)Spring配置文件引入到我們的applicationContext文件中东羹。
比如我們針對(duì)數(shù)據(jù)庫配置剂桥,緩存配置,MQ配置属提,日志配置等模塊权逗,為每個(gè)模塊配置相應(yīng)的文件:
applicationContext-cache.xml
applicationContext-db.xml
applicationContext-mp.xml
applicationContext-log.xml
然后在applicationContext配置文件中使用import
進(jìn)行引入:
<?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-autowire="byType">
<import resource="classpath*:spring/applicationContext-cache.xml"/>
<import resource="classpath*:spring/applicationContext-db.xml"/>
<import resource="classpath*:spring/applicationContext-log.xml"/>
<import resource="classpath*:spring/applicationContext-mq.xml"/>
</beans>
這樣,當(dāng)我們需要新加配置的時(shí)候冤议,只需要添加對(duì)應(yīng)的配置文件斟薇,然后再使用import
標(biāo)簽進(jìn)行引入即可。這里再簡單說下恕酸,resource的幾種選擇:
<import resource="applicationContext-test.xml"/>
堪滨,默認(rèn)情況下,Spring會(huì)根據(jù)相對(duì)路徑來加載對(duì)應(yīng)的配置文件蕊温;<import resource="classpath:"/>
袱箱,配置為classpath
的情況下遏乔,是去class路徑及相應(yīng)的jar包下加載。而classpath:
為精確配置发笔,只能匹配一個(gè)盟萨,而classpath*:
是一種通配符的形式,可以加載多個(gè)了讨;<import resource="file:"/>
捻激,file是加載一個(gè)或多個(gè)文件系統(tǒng)的resource,比如file:D:/*.txt
將加載D盤下所有的txt文件前计;<import resource="http:"/>
铺罢,http就是從網(wǎng)絡(luò)下加載相應(yīng)的resource;
針對(duì)classpath残炮,后續(xù)我們會(huì)再仔細(xì)討論這個(gè)問題韭赘,現(xiàn)在先大致了解下即可。
參考資料:
Spring中的destroy-method方法詳解
https://docs.spring.io/spring/docs/4.3.14/spring-framework-reference/beans.html