1、spring容器成功啟動的條件
spring架構的類包都已放在應用程式的類路徑;
應用程式為spring提供完備的bean資訊;
bean的類都已放到應用程式的類路徑下。
2、bean配置資訊是bean的元素據資訊,由4個方面組成:
bean的實作類;
bean的屬性資訊(如資料源的連接配接數、使用者名、密碼);
bean的依賴關系,spring根據依賴關系配置完成bean間的裝配;
bean的行為配置(如生命周期範圍及生命周期各過程的回調函數)。
2.3.1 bean基本配置
bean命名
id為bean的名稱,通過容器的getbean("foo")擷取對應的bean,在容器中起到定位查找的作用,是外部程式和spring ioc容器進行互動的橋梁,class指定了bean對應的實作類。
id(xml規定的特殊屬性)在ioc容器中必須唯一,還需滿足xml對id的命名規範:字母開始,後接字母、數字、連字元、下劃線、句号、冒号等完整結束符。逗号、空格這些非完整結束符是非法的。
name屬性幾乎可用任何字元,
<bean name="#car" class="com.car"/>
id和name都可指定多個名字,名字間用逗号、分号或空格分隔。使用者可用getbean("123")或另外兩種名字擷取ioc容器中的carbean。
<bean name="#car,123,$car" class="com.car"/>
spring配置檔案不允許兩個相同id的<bean>;但允許相同name的<bean>,getbean時會傳回最後聲明的那個bean,後面的bean覆寫了前面同名的bean。so,盡量使用id而不是name命名的bean。
如果id、name都未指定,如①<bean class="com.car"/>,spring自動将全限定類名作為bean的名稱,可通過②getbean("com.car")擷取carbean,如果存在多個實作類相同的匿名<bean>即多個①處的bean,第一個bean通過②獲得,第二個bean通過getbean("com.car#1")獲得,以此類推。
2.3.2 依賴注入
1)屬性注入
setxxx()方法,可選擇性、靈活性高,最常用。
要求bean提供預設的構造函數,并未需要注入的屬性提供對應的setter方法。spring先調用bean的預設 構造函數執行個體化bean對象,然後通過反射調用setter方法注入屬性值。
【預設構造函數】
預設構造函數不帶參。java規定如果類中沒有定義任何構造函數,jvm自動生成一個預設構造函數。反之,如果類中顯示定義了構造函數,jvm将不會自動生成。so,類中顯示定義了一個帶參構造函數,則需同時提供一個預設構造函數,否則屬性注入時将抛出異常。
spring隻會檢查bean中是否有對應的setter,是否有對應屬性變量則不作要求。但一般約定俗成在bean中提供同名的屬性變量。
【java屬性變量】
一般以小寫字母起頭,javabean允許大寫字母開頭,但必須滿足“變量的前兩個字母要麼全部大寫,要麼全部小寫”,brand、idcode合法,ic、idcode非法。
2)構造函數注入
保證一些必要屬性在bean執行個體化時就得到設定,并確定bean執行個體在執行個體化後就可以使用。使用構造函數的前提是bean必須提供帶參的構造函數。
spring中構造函數注入的配置方式
注入時若比對的構造函數不止一個,預設比對最後一個構造函數。
bean存在多個構造函數時,盡可能顯示指定index、type。
①bytype:
<bean id="car1" class="com.smart.injection.car">
<constructor-arg type="java.lang.string">
<value>紅旗ca72</value>
</constructor-arg>
<constructor-arg type="double">
<value>20000</value>
<!--car
bean中必須有和此處一緻的構造函數(string、double參數的構造函數)-->
</bean>
②byindex:
<!--構造注入-索引比對入參-->
<bean id="carindex" class="com.smart.injection.car">
<!--索引從0開始-->
<constructor-arg index="0" value="紅旗ca72"/>
<constructor-arg index="1" value="中國一汽"/>
<constructor-arg index="2" value="20000"/>
<!-- 必須有一個3個入參的構造函數,如果不止一個,将以最後一個構造函數為準 -->
③bytypeindex:
3)循環依賴問題
spring容器順利執行個體化以構造函數方式配置的bean的前提是:bean構造函數引用的對象已經就緒。
如果兩個bean均通過構造函數注入,且通過構造函數入參引用對方,就會發生類似線程死鎖的循環依賴問題。
2.3.3 注入參數詳解
①字面值--可通過<value>元素标簽注入
字面值一般指可用字元串表示的值,如基本資料類型及其封裝類、string。spring内部編輯器可将以字元串表示的字面值轉化為内部變量的相應類型。
特殊處理标簽:
②引用其他bean--<ref>
<ref>元素引用bean的3個屬性:
bean:引用同一容器或父容器的bean,最常見。
local:隻能引用同一配置檔案的bean,可利用xml解析器自動檢驗引用的合法性。
parent:引用父容器的bean。
③集合類型屬性
主要包括list、set、map、properties。
④通過util命名空間配置集合類型的bean
⑤簡化配置方式
⑤自動裝配 autowire=“<自動裝配類型>”
spring ioc容器了解容器中所有的bean資訊,通過java反射機制獲知實作類的結構資訊,開發人員可按照規則進行bean的字段裝配。
spring提供了4種自動裝配類型。<beans>元素标簽中的default-autowired屬性可配置為全局自動比對,預設為0,表示不啟用自動裝配。其他可選配置如下圖,byname,bytype,constructor,autodetect。
減輕配置工作量,但造成配置檔案bean間關系不清楚,易引發潛在錯誤,是以實際項目建議少用。
2.3.4 bean作用域
spring2.0前僅有2個作用域:singleton和prototype。2.0開始針對webapplicationcontext新添了3個作用域。
spring低版本由于隻有2個作用域,so采用singleton=“true|false”的配置方式,為向後相容,現依舊可用。
spring新配置方式:scope=“<作用域類型>”
<bean id="accountservice" class="com.foo.defaultaccountservice" scope="singleton"/>
除以上5種,spring還允許使用者自定義bean的作用域。org.springframework.beans.factory.config.scope接口定義新作用域,再通過org。springframework.beans.factory.config.customscopeconfigurer這個beanfactorypostprocessor注冊自定義的bean作用域。【很少需要使用者自定義作用域,自帶的足以滿足大部分需求】
2.3.5 基于注解的配置
1)使用注解定義bean
基于xml的配置,bean定義資訊和bean實作類本身分離;
基于注解的配置,bean定義資訊通過在bean實作類上标注注解實作。
@component("userdao")
public class userdao{……}
等價于一下xml配置:
<bean id="userdao" class="com.smart.userdao"/>
除@component外,spring還提供3個功能基本和其等效的注解,也稱為bean的衍型注解。
@repository:标注dao實作類
@service:标注service實作類
@controller:标注web層的controller實作類衍型注解讓注解本身用途清晰化,也被spring賦予了一些特殊的功能。【推薦使用】
2)使用注解配置資訊啟動spring容器spring2.5後提供了context命名空間,提供了提供掃描包以應用注解定義bean的方式。
使用方式:
a、聲明context命名空間,xmlns:context="http://www.springframework.org/schema/context"
b、掃描類包以應用注解定義的bean,<context:component-scanbase-package="com.smart.anno">
context的命名空間的component-scan的base-package屬性指定一個需要掃描的基類包,spring将掃描此基類包的所有類,并從注解資訊中擷取bean資訊。
c、resource-pattern:過濾出特定類,僅掃描特定類而非基類包下的所有類。
<context:component-scanbase-package="com.smart" resource-pattern="anno/*.class">
resource-pattern屬性預設值為“**/*.class”,即基類包裡的所有類。此處設定為"anno/*.class",spring僅會掃描基類包anno子包中的類。
d、<context:component-scan>過濾子元素,如僅過濾基類包中實作了xxxservice接口的類或标注了某個特定注解的類等,而resource-pattern僅可按資源名稱過濾。
<context:include-filter>表示要包含的類,<context:exclude-filter>表示要排除的類;一個component-scan下可包含若幹個exclude和include。且這兩個過濾元素均支援多種類型的過濾表達式。
過濾類型中,除custom外,aspectj過濾表達能力最強,可輕易實作其他類型所表達的過濾規則。
3)自動裝配bean【@autowired】
private userdao userdao;
}
@autowired:預設按類型比對,當有且僅有一個比對的bean時,spring将其注入@autowired标注的變量中。
①@autowired的requried屬性
若容器中沒有和按标注類型比對的bean,spring将抛出nosuchbeandefinitionexception異常,如果希望即使找不到比對的bean也不要抛出異常,可使用@autowired(required = false)進行标注。【required預設值為true】
②@qualifier指定注入bean的名稱
如果有超過一個比對的bean,可通過@qualifier注解限定bean的名稱。
@autowired
@qualifier("userdao") //①
假設容器有兩個類型userdao的bean,一個名為userdao,一個名為userdao2,則①處會注入名為userdao的bean。
③标注類方法
@autowired 可标注類成員變量及方法的入參,亦可标注類。
@autowired //自動将logdao傳給方法入參
public void setlogdao(logdao logdao) {
this.logdao = logdao;
@autowired // 自動将,名為userdao的bean傳給方法入參
@qualifier("userdao")
public void setuserdao(userdao userdao) {
this.userdao = userdao;
如果一個方法擁有多個入參,預設情況下,spring自動選擇比對入參類型的bean進行注入,spring允許使用@qualifier指定注入入參的bean的名稱。
public void init(@qualifier("userdao")userdao userdao,logdao logdao){
this.userdao = userdao;
this.logdao =logdao;
以上例子,userdao的入參注入名為userdao的bean,而logdao的入參注入logdao類型的bean。
note:
通常,spring容器的大部分bean是單執行個體,so無需通過@repository、@service等注解的value屬性指定名稱,也無需使用@qualifier按名稱注入。
④标注集合類
此處,plugin為接口,有兩個實作類且兩個實作類都通過@component标注為bean,so spring會将這兩個bean都注入plugins。
2.3.6 基于java類的配置
1)使用java類提供bean定義資訊
@configuration// 此注解說明此類可用于為spring提供bean的定義資訊
public class appconf {
@bean // 将一個pojo标注為定義bean的配置類
public userdao userdao() {
return new userdao();
}
@bean // bean類型由方法傳回值類型決定,名稱預設和方法名相同,亦可顯示指定。
public logdao logdao() {
return new logdao();
@bean(name="logonservice")
public logonservice logonservice() {
logonservice logonservice = new logonservice();
logonservice.setlogdao(logdao()); // 注入上面定義的bean
logonservice.setuserdao(userdao());
return logonservice;
以上配置等價于
<bean id="userdao" class="com.smart.anno.userdao"/>
<bean id="logdao" class="com.smart.anno.logdao"/>
<bean id="logonservice" class="com.smart.conf.logonservice"
p:logdao-ref="userdao" p:userdao-ref="logdao"/>
對比:
基于java類的配置方式:更靈活地實作bean的執行個體化及bean之間的裝配;
xml或基于注解: 通過配置聲明,靈活性略遜,但配置更簡單。
@configuration
public class daoconfig {
@bean
public userdao userdao(){
@scope("prototype")
public logdao logdao(){
------------------------
public class serviceconfig {
@autowired
private daoconfig daoconfig;
system.out.println(daoconfig.logdao() == daoconfig.logdao());
logonservice.setlogdao(daoconfig.logdao()); //a、像普通bean一樣調用其他bean方法
logonservice.setuserdao(daoconfig.userdao());
@configuration注解類本身相當于标注了@component,即可像普通bean一樣被注入其他bean中(可直接調用@configuration中标注@bean的方法,如上)。
spring對配置類所有标注@bean的方法進行aop增強,将對聲明周期的邏輯植入進來。
a處調用daoconfig.logdao()邏輯:從spring容器傳回相應bean的單例,即多次調用daoconfig.logdao()傳回的都是相同bean。bean若标注@scope("prototype"),則每次傳回新的bean。
2)直接通過@configuration類啟動spring容器
// 使用@configuration類中提供的bean定義資訊啟動spring容器
applicationcontext ctx = new annotationconfigapplicationcontext(appconf.class); //直接傳入@configuration标注的java類
logonservice logonservice = ctx.getbean(logonservice.class);
注:
annotationconfigapplicationcontext支援加載多個配置類
//2.通過編碼方式注冊配置類
annotationconfigapplicationcontext ctx2 = new annotationconfigapplicationcontext();
ctx2.register(daoconfig.class);
ctx2.register(serviceconfig.class);
ctx2.refresh(); //重新整理容器以應用這些注冊的配置類
//3.@import将多個配置類組裝到一個配置類中,僅需注冊這個組裝好的配置類即可啟動容器。
@import(daoconfig.class)
3)xml配置檔案引用@configuration的配置
// 通過上下文掃描加載到appconf的配置類
<context:component-scan base-package="com.smart.conf"
resource-pattern="appconf.class" />
4)通過configuration配置類引用xml配置資訊
beans3.xml:
<bean id="userdao" class="com.smart.conf.userdao"/>
2.3.7 基于xml、注解、java類三種配置方式的比較
通常基于java類的方式使用較少。