轉
Spring****基于 Annotation 的簡單介紹
2011年03月29日 15:07:00
閱讀數:49983
1.****使用 @Repository荒叶、@Service糠馆、@Controller 和 @Component 將類標識為 Bean:
Spring 自 2.0 版本開始味悄,陸續(xù)引入了一些注解用于簡化 Spring 的開發(fā)。@Repository 注解便屬于最先引入的一批旗国,它用于將數據訪問層 (DAO 層 ) 的類標識為 Spring Bean定嗓。具體只需將該注解標注在 DAO 類上即可蜕琴。同時,為了讓 Spring 能夠掃描類路徑中的類并識別出 @Repository 注解宵溅,需要在 XML 配置文件中啟用 Bean 的自動掃描功能凌简,這可以通過 <context:component-scan/> 實現。如下所示:
首先恃逻,使用 @Repository 將 DAO 類聲明為 Bean:
[java] view plain copy
// 首先使用 @Repository 將 DAO 類聲明為 Bean
package bookstore.dao;
@Repository
public class UserDaoImpl implements UserDao{ …… }
// 其次雏搂,在 XML 配置文件中啟動 Spring 的自動掃描功能
<beans … >
……
<context:component-scan base-package=”bookstore.dao” />
……
</beans>
其次,在 XML 配置文件中啟動 Spring 的自動掃描功能:
[xhtml] view plain copy
<beans … >
……
<context:component-scan base-package=”bookstore.dao” />
…
</beans>
如此寇损,我們就不再需要在 XML 中顯式使用 <bean/> 進行 Bean 的配置凸郑。Spring 在容器初始化時將自動掃描 base-package 指定的包及其子包下的所有 class 文件,所有標注了 @Repository 的類都將被注冊為 Spring Bean矛市。
為什么 @Repository 只能標注在 DAO 類上呢芙沥?這是因為該注解的作用不只是將類識別為 Bean,同時它還能將所標注的類中拋出的數據訪問異常封裝為 Spring 的數據訪問異常類型浊吏。 Spring 本身提供了一個豐富的并且是與具體的數據訪問技術無關的數據訪問異常結構而昨,用于封裝不同的持久層框架拋出的異常,使得異常獨立于底層的框架找田。
2.Spring 2.5 在 @Repository 的基礎上增加了功能類似的額外三個注解:
@Component歌憨、@Service、@Constroller墩衙,它們分別用于軟件系統的不同層次:
· @Component 是一個泛化的概念务嫡,僅僅表示一個組件 (Bean) ,可以作用在任何層次底桂。
· @Service 通常作用在業(yè)務層植袍,但是目前該功能與 @Component 相同惧眠。
· @Constroller 通常作用在控制層籽懦,但是目前該功能與 @Component 相同。
通過在類上使用 @Repository氛魁、@Component暮顺、@Service 和 @Constroller 注解,Spring 會自動創(chuàng)建相應的 BeanDefinition 對象秀存,并注冊到 ApplicationContext 中捶码。這些類就成了 Spring 受管組件。這三個注解除了作用于不同軟件層次的類或链,其使用方式與 @Repository 是完全相同的惫恼。
另外,除了上面的四個注解外澳盐,用戶可以創(chuàng)建自定義的注解祈纯,然后在注解上標注 @Component令宿,那么,該自定義注解便具有了與所 @Component 相同的功能腕窥。不過這個功能并不常用粒没。
3.Spring****使用注解的機制:
當一個 Bean 被自動檢測到時,會根據那個掃描器的 BeanNameGenerator 策略生成它的 bean 名稱簇爆。默認情況下癞松,對于包含 name 屬性的 @Component、@Repository入蛆、 @Service 和 @Controller响蓉,會把 name 取值作為 Bean 的名字。如果這個注解不包含 name 值或是其他被自定義過濾器發(fā)現的組件哨毁,默認 Bean 名稱會是小寫開頭的非限定類名厕妖。如果你不想使用默認 bean 命名策略,可以提供一個自定義的命名策略挑庶。首先實現 BeanNameGenerator 接口言秸,確認包含了一個默認的無參數構造方法。然后在配置掃描器時提供一個全限定類名迎捺,如下所示:
[xhtml] view plain copy
<beans ...>
<context:component-scan
base-package="a.b" name-generator="a.SimpleNameGenerator"**/>**
</beans>
與通過 XML 配置的 Spring Bean 一樣举畸,通過上述注解標識的 Bean,其默認作用域是"singleton"凳枝,為了配合這四個注解抄沮,在標注 Bean 的同時能夠指定 Bean 的作用域,Spring 2.5 引入了 @Scope 注解岖瑰。使用該注解時只需提供作用域的名稱就行了叛买,如下所示:
[java] view plain copy
@Scope("prototype")
@Repository
public class Demo { … }
如果你想提供一個自定義的作用域解析策略而不使用基于注解的方法,只需實現 ScopeMetadataResolver 接口蹋订,確認包含一個默認的沒有參數的構造方法率挣。然后在配置掃描器時提供全限定類名:
[xhtml] view plain copy
<context:component-scan base-package="a.b"
scope-resolver="footmark.SimpleScopeResolver" />
4.****使用 @PostConstruct 和 @PreDestroy 指定生命周期回調方法:
Spring Bean 是受 Spring IoC 容器管理,由容器進行初始化和銷毀的(prototype 類型由容器初始化之后便不受容器管理)露戒,通常我們不需要關注容器對 Bean 的初始化和銷毀操作椒功,由 Spring 經過構造函數或者工廠方法創(chuàng)建的 Bean 就是已經初始化完成并立即可用的。然而在某些情況下智什,可能需要我們手工做一些額外的初始化或者銷毀操作动漾,這通常是針對一些資源的獲取和釋放操作。Spring 1.x 為此提供了兩種方式供用戶指定執(zhí)行生命周期回調的方法荠锭。
第一種方式:
是實現 Spring 提供的兩個接口:InitializingBean 和 DisposableBean旱眯。如果希望在 Bean 初始化完成之后執(zhí)行一些自定義操作,則可以讓 Bean 實現 InitializingBean 接口,該接口包含一個 afterPropertiesSet() 方法删豺,容器在為該 Bean 設置了屬性之后础爬,將自動調用該方法;如果 Bean 實現了 DisposableBean 接口吼鳞,則容器在銷毀該 Bean 之前看蚜,將調用該接口的 destroy() 方法。這種方式的缺點是赔桌,讓 Bean 類實現 Spring 提供的接口供炎,增加了代碼與 Spring 框架的耦合度,因此不推薦使用疾党。
第二種方式是:
在 XML 文件中使用 <bean> 的 init-method 和 destroy-method 屬性指定初始化之后和銷毀之前的回調方法音诫,代碼無需實現任何接口。這兩個屬性的取值是相應 Bean 類中的初始化和銷毀方法雪位,方法名任意竭钝,但是方法不能有參數。示例如下:
[xhtml] view plain copy
<bean id=”userService”
class=”bookstore.service.UserService”
init-method=”init” destroy-method=”destroy”>
…
</bean>
第三種方式:
Spring 2.5 在保留以上兩種方式的基礎上雹洗,提供了對 JSR-250 的支持香罐。JSR-250 規(guī)范定義了兩個用于指定聲明周期方法的注解:@PostConstruct 和 @PreDestroy。這兩個注解使用非常簡單时肿,只需分別將他們標注于初始化之后執(zhí)行的回調方法或者銷毀之前執(zhí)行的回調方法上庇茫。由于使用了注解,因此需要配置相應的 Bean 后處理器螃成,亦即在 XML 中增加如下一行:
[xhtml] view plain copy
- <context:annotation-config />
比較上述三種指定生命周期回調方法的方式旦签,第一種是不建議使用的,不但其用法不如后兩種方式靈活寸宏,而且無形中增加了代碼與框架的耦合度宁炫。后面兩種方式開發(fā)者可以根據使用習慣選擇其中一種,但是最好不要混合使用氮凝,以免增加維護的難度羔巢。
5.****使用 @Required 進行 Bean 的依賴檢查:
依賴檢查的作用是,判斷給定 Bean 的相應 Setter 方法是否都在實例化的時候被調用了覆醇。而不是判斷字段是否已經存在值了朵纷。Spring 進行依賴檢查時,只會判斷屬性是否使用了 Setter 注入永脓。如果某個屬性沒有使用 Setter 注入,即使是通過構造函數已經為該屬性注入了值鞋仍,Spring 仍然認為它沒有執(zhí)行注入常摧,從而拋出異常。另外,Spring 只管是否通過 Setter 執(zhí)行了注入落午,而對注入的值卻沒有任何要求谎懦,即使注入的 <null/>,Spring 也認為是執(zhí)行了依賴注入溃斋。
<bean> 標簽提供了 dependency-check 屬性用于進行依賴檢查界拦。該屬性的取值包括以下幾種:
· none -- 默認不執(zhí)行依賴檢查」=伲可以在 <beans> 標簽上使用 default-dependency-check 屬性改變默認值享甸。
· simple -- 對原始基本類型和集合類型進行檢查。
· objects -- 對復雜類型進行檢查(除了 simple 所檢查類型之外的其他類型)梳侨。
· all -- 對所有類型進行檢查蛉威。
舊版本使用 dependency-check 在配置文件中設置,缺點是粒度較粗走哺。使用 Spring2.0 提供的 @Required 注解蚯嫌,提供了更細粒度的控制。@Required 注解只能標注在 Setter 方法之上丙躏。因為依賴注入的本質是檢查 Setter 方法是否被調用了择示,而不是真的去檢查屬性是否賦值了以及賦了什么樣的值。如果將該注解標注在非 setXxxx() 類型的方法則被忽略晒旅。
為了讓 Spring 能夠處理該注解对妄,需要激活相應的 Bean 后處理器。要激活該后處理器敢朱,只需在 XML 中增加如下一行即可:
[xhtml] view plain copy
- <context:annotation-config/>
當某個被標注了 @Required 的 Setter 方法沒有被調用剪菱,則 Spring 在解析的時候會拋出異常,以提醒開發(fā)者對相應屬性進行設置拴签。
6.****使用 @Resource孝常、@Autowired 和 @Qualifier 指定 Bean 的自動裝配策略:
自動裝配是指,Spring 在裝配 Bean 的時候蚓哩,根據指定的自動裝配規(guī)則构灸,將某個 Bean 所需要引用類型的 Bean 注入進來。<bean> 元素提供了一個指定自動裝配類型的 autowire 屬性岸梨,該屬性有如下選項:
· no -- 顯式指定不使用自動裝配喜颁。
· byName -- 如果存在一個和當前屬性名字一致的 Bean,則使用該 Bean 進行注入曹阔。如果名稱匹配但是類型不匹配半开,則拋出異常。如果沒有匹配的類型赃份,則什么也不做寂拆。
· byType -- 如果存在一個和當前屬性類型一致的 Bean ( 相同類型或者子類型 )奢米,則使用該 Bean 進行注入。byType 能夠識別工廠方法纠永,即能夠識別 factory-method 的返回類型鬓长。如果存在多個類型一致的 Bean,則拋出異常尝江。如果沒有匹配的類型涉波,則什么也不做。
· constructor -- 與 byType 類似炭序,只不過它是針對構造函數注入而言的啤覆。如果當前沒有與構造函數的參數類型匹配的 Bean,則拋出異常少态。使用該種裝配模式時城侧,優(yōu)先匹配參數最多的構造函數。
· autodetect -- 根據 Bean 的自省機制決定采用 byType 還是 constructor 進行自動裝配彼妻。如果 Bean 提供了默認的構造函數嫌佑,則采用 byType;否則采用 constructor 進行自動裝配侨歉。
當使用 byType 或者 constructor 類型的自動裝配的時候屋摇,自動裝配也支持引用類型的數組或者使用了泛型的集合,這樣幽邓,Spring 就會檢查容器中所有類型匹配的 Bean炮温,組成集合或者數組后執(zhí)行注入。對于使用了泛型的 Map 類型牵舵,如果鍵是 String 類型柒啤,則 Spring 也會自動執(zhí)行裝配,將所有類型匹配的 Bean 作為值畸颅,Bean 的名字作為鍵担巩。
我們可以給 <beans> 增加 default-autowire 屬性,設置默認的自動封裝策略没炒。默認值為"no"涛癌。如果使用自動裝配的同時,也指定了 property 或者 constructor-arg 標簽送火,則顯式指定的值將覆蓋自動裝配的值拳话。目前的自動封裝不支持簡單類型,比如基本類型种吸、String弃衍、Class,以及它們的數組類型骨稿。
在按類型匹配的時候 ( 可能是 byType笨鸡、constructor姜钳、autodetect)坦冠,同一個類型可能存在多個 Bean形耗,如果被注入的屬性是數組、集合或者 Map辙浑,這可能沒有問題激涤,但是如果只是簡單的引用類型,則會拋出異常判呕。解決方法有如下幾種:
· 取消該 Bean 的自動裝配特性倦踢,使用顯式的注入。我們可能不希望某個 Bean 被當作其他 Bean 執(zhí)行自動封裝時的候選對象侠草,我們可以給該 <bean> 增加 autowire-candidate="false"辱挥。(autowire-candidate 屬性和 autowire 屬性相互獨立,互不相干边涕。某個 Bean 可以將 autowire-candidate 設置為 false晤碘,同時使用 autowire 特性。) 另外功蜓,我們可以設置 <beans> 的 default-autowire-candidates 屬性园爷,可以在該屬性中指定可以用于自動裝配候選 Bean 的匹配模式,比如 default-autowire-candidates="serv,dao"式撼,這表示所有名字以 serv 或者 dao 結尾的 Bean 被列為候選童社,其他則忽略,相當于其他 Bean 都指定為 autowire-candidate="false"著隆,此時可以顯式為 <bean> 指定 autowire-candidate="true"扰楼。在 <bean> 上指定的設置要覆蓋 <beans> 上指定的設置。
· 如果在多個類型相同的 Bean 中有首選的 Bean美浦,那么可以將該 <bean> 的 primary 屬性設置為 "true" 弦赖,這樣自動裝配時便優(yōu)先使用該 Bean 進行裝配。此時不能將 autowire-candidate 設為 false抵代。
· 如果使用的是 Java 5 以上版本腾节,可以使用注解進行更細粒度的控制。
7.****使用 @Autowired 和 @Qualifier 注解執(zhí)行自動裝配:
使用 @Autowired 注解進行裝配荤牍,只能是根據類型進行匹配案腺。@Autowired 注解可以用于 Setter 方法、構造函數康吵、字段央渣,甚至普通方法入录,前提是方法必須有至少一個參數。@Autowired 可以用于數組和使用泛型的集合類型父叙。然后 Spring 會將容器中所有類型符合的 Bean 注入進來。@Autowired 標注作用于 Map 類型時晦毙,如果 Map 的 key 為 String 類型,則 Spring 會將容器中所有類型符合 Map 的 value 對應的類型的 Bean 增加進來,用 Bean 的 id 或 name 作為 Map 的 key响巢。
@Autowired 標注作用于普通方法時,會產生一個副作用棒妨,就是在容器初始化該 Bean 實例的時候就會調用該方法踪古。當然,前提是執(zhí)行了自動裝配券腔,對于不滿足裝配條件的情況伏穆,該方法也不會被執(zhí)行。
當標注了 @Autowired 后纷纫,自動注入不能滿足枕扫,則會拋出異常。我們可以給 @Autowired 標注增加一個 required=false 屬性辱魁,以改變這個行為烟瞧。另外,每一個類中只能有一個構造函數的 @Autowired.required() 屬性為 true商叹。否則就出問題了燕刻。如果用 @Autowired 同時標注了多個構造函數,那么剖笙,Spring 將采用貪心算法匹配構造函數 ( 構造函數最長 )卵洗。
@Autowired 還有一個作用就是,如果將其標注在 BeanFactory 類型弥咪、ApplicationContext 類型过蹂、ResourceLoader 類型、ApplicationEventPublisher 類型聚至、MessageSource 類型上酷勺,那么 Spring 會自動注入這些實現類的實例,不需要額外的操作扳躬。
當容器中存在多個 Bean 的類型與需要注入的相同時脆诉,注入將不能執(zhí)行,我們可以給 @Autowired 增加一個候選值贷币,做法是在 @Autowired 后面增加一個 @Qualifier 標注击胜,提供一個 String 類型的值作為候選的 Bean 的名字。舉例如下:
[java] view plain copy
@Autowired(required=false)
@Qualifier("ppp")
public void setPerson(person p){}
@Qualifier 甚至可以作用于方法的參數 ( 對于方法只有一個參數的情況役纹,我們可以將 @Qualifer 標注放置在方法聲明上面偶摔,但是推薦放置在參數前面 ),舉例如下:
[java] view plain copy
@Autowired(required=false)
public void sayHello(@Qualifier("ppp")Person p,String name){}
我們可以在配置文件中指定某個 Bean 的 qualifier 名字促脉,方法如下:
[xhtml] view plain copy
<bean id="person" class="footmark.spring.Person">
<qualifier value="ppp"/>
</bean>
如果沒有明確指定 Bean 的 qualifier 名字辰斋,那么默認名字就是 Bean 的名字策州。通常,qualifier 應該是有業(yè)務含義的宫仗,例如 "domain"够挂,"persistent" 等,而不應該是類似 "person" 方式锰什。
我們還可以將 @Qualifier 標注在集合類型上下硕,那么所有 qualifier 名字與指定值相同的 Bean 都將被注入進來丁逝。
最后汁胆,配置文件中需要指定每一個自定義注解的屬性值。我們可以使用 <meta> 標簽來代替 <qualifier/> 標簽霜幼,如果 <meta> 標簽和 <qualifier/> 標簽同時出現嫩码,那么優(yōu)先使用 <qualifier> 標簽。如果沒有 <qualifier> 標簽罪既,那么會用 <meta> 提供的鍵值對來封裝 <qualifier> 標簽铸题。示例如下:
[xhtml] view plain copy
<bean class="footmark.HelloWorld">
<qualifier type="MovieQualifier">
<attribute key="format" value="VHS"/>
<attribute key="genre" value="Comedy"/>
</qualifier>
</bean>
<bean class="footmark.HelloWorld">
<meta key="format" value="DVD"/>
<meta key="genre" value="Action"/>
</bean>
@Autowired 注解對應的后處理注冊與前面相似,只需在配置文件中增加如下一行即可:
[xhtml] view plain copy
- <context:annotation-config/>
如果 @Autowired 注入的是 BeanFactory琢感、ApplicationContext丢间、ResourceLoader 等系統類型,那么則不需要 @Qualifier驹针,此時即使提供了 @Qualifier 注解烘挫,也將會被忽略;而對于自定義類型的自動裝配柬甥,如果使用了 @Qualifier 注解并且沒有名字與之匹配的 Bean饮六,則自動裝配匹配失敗。
8.****使用@Resource 和 @Qualifier 注解:
如果希望根據 name 執(zhí)行自動裝配苛蒲,那么應該使用 JSR-250 提供的 @Resource 注解卤橄,而不應該使用 @Autowired 與 @Qualifier 的組合。
@Resource 使用 byName 的方式執(zhí)行自動封裝臂外。@Resource 標注可以作用于帶一個參數的 Setter 方法窟扑、字段,以及帶一個參數的普通方法上漏健。@Resource 注解有一個 name 屬性嚎货,用于指定 Bean 在配置文件中對應的名字。如果沒有指定 name 屬性漾肮,那么默認值就是字段或者屬性的名字厂抖。@Resource 和 @Qualifier 的配合雖然仍然成立,但是 @Qualifier 對于 @Resource 而言克懊,幾乎與 name 屬性等效忱辅。
如果 @Resource 沒有指定 name 屬性七蜘,那么使用 byName 匹配失敗后,會退而使用 byType 繼續(xù)匹配墙懂,如果再失敗橡卤,則拋出異常。在沒有為 @Resource 注解顯式指定 name 屬性的前提下损搬,如果將其標注在 BeanFactory 類型碧库、ApplicationContext 類型、ResourceLoader 類型巧勤、ApplicationEventPublisher 類型嵌灰、MessageSource 類型上,那么 Spring 會自動注入這些實現類的實例颅悉,不需要額外的操作沽瞭。此時 name 屬性不需要指定 ( 或者指定為""),否則注入失斒F俊驹溃;如果使用了 @Qualifier,則該注解將被忽略延曙。而對于用戶自定義類型的注入豌鹤,@Qualifier 和 name 等價,并且不被忽略枝缔。
<bean> 的 primary 和 autowire-candidate 屬性對 @Resource布疙、@Autowired 仍然有效。
9.****使用 @Configuration 和 @Bean 進行 Bean 的聲明:
雖然 2.0 版本發(fā)布以來魂仍,Spring 陸續(xù)提供了十多個注解拐辽,但是提供的這些注解只是為了在某些情況下簡化 XML 的配置,并非要取代 XML 配置方式擦酌。這一點可以從 Spring IoC 容器的初始化類可以看出:ApplicationContext 接口的最常用的實現類是 ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext俱诸,以及面向 Portlet 的 XmlPortletApplicationContext 和面向 web 的 XmlWebApplicationContext,它們都是面向 XML 的赊舶。Spring 3.0 新增了另外兩個實現類:AnnotationConfigApplicationContext 和 AnnotationConfigWebApplicationContext睁搭。從名字便可以看出,它們是為注解而生笼平,直接依賴于注解作為容器配置信息來源的 IoC 容器初始化類园骆。由于 AnnotationConfigWebApplicationContext 是 AnnotationConfigApplicationContext 的 web 版本,其用法與后者相比幾乎沒有什么差別寓调,因此本文將以 AnnotationConfigApplicationContext 為例進行講解锌唾。
AnnotationConfigApplicationContext 搭配上 @Configuration 和 @Bean 注解,自此,XML 配置方式不再是 Spring IoC 容器的唯一配置方式晌涕。兩者在一定范圍內存在著競爭的關系滋捶,但是它們在大多數情況下還是相互協作的關系,兩者的結合使得 Spring IoC 容器的配置更簡單余黎,更強大重窟。
之前,我們將配置信息集中寫在 XML 中惧财,如今使用注解巡扇,配置信息的載體由 XML 文件轉移到了 Java 類中。我們通常將用于存放配置信息的類的類名以 “Config” 結尾垮衷,比如 AppDaoConfig.java厅翔、AppServiceConfig.java 等等。我們需要在用于指定配置信息的類上加上 @Configuration 注解帘靡,以明確指出該類是 Bean 配置的信息源知给。并且 Spring 對標注 Configuration 的類有如下要求:
· 配置類不能是 final 的;
· 配置類不能是本地化的描姚,亦即不能將配置類定義在其他類的方法內部;
· 配置類必須有一個無參構造函數戈次。
AnnotationConfigApplicationContext 將配置類中標注了 @Bean 的方法的返回值識別為 Spring Bean轩勘,并注冊到容器中,受 IoC 容器管理怯邪。@Bean 的作用等價于 XML 配置中的 <bean/> 標簽绊寻。示例如下:
[java] view plain copy
@Configuration
public class BookStoreDaoConfig{
@Bean
**public** UserDao userDao(){ **return** **new** UserDaoImpl();}
@Bean
**public** BookDao bookDao(){**return** **new** BookDaoImpl();}
}
Spring 在解析到以上文件時,將識別出標注 @Bean 的所有方法悬秉,執(zhí)行之澄步,并將方法的返回值 ( 這里是 UserDaoImpl 和 BookDaoImpl 對象 ) 注冊到 IoC 容器中。默認情況下和泌,Bean 的名字即為方法名村缸。因此,與以上配置等價的 XML 配置如下:
[xhtml] view plain copy
<bean id=”userDao” class=”bookstore.dao.UserDaoImpl”/>
<bean id=”bookDao” class=”bookstore.dao.BookDaoImpl”/>
@Bean 具有以下四個屬性:
· name -- 指定一個或者多個 Bean 的名字武氓。這等價于 XML 配置中 <bean> 的 name 屬性梯皿。
· initMethod -- 容器在初始化完 Bean 之后,會調用該屬性指定的方法县恕。這等價于 XML 配置中 <bean> 的 init-method 屬性东羹。
· destroyMethod -- 該屬性與 initMethod 功能相似,在容器銷毀 Bean 之前忠烛,會調用該屬性指定的方法属提。這等價于 XML 配置中 <bean> 的 destroy-method 屬性。
· autowire -- 指定 Bean 屬性的自動裝配策略美尸,取值是 Autowire 類型的三個靜態(tài)屬性冤议。Autowire.BY_NAME旬迹,Autowire.BY_TYPE,Autowire.NO求类。與 XML 配置中的 autowire 屬性的取值相比奔垦,這里少了 constructor,這是因為 constructor 在這里已經沒有意義了尸疆。
@Bean 沒有直接提供指定作用域的屬性椿猎,可以通過 @Scope 來實現該功能,關于 @Scope 的用法已在上文列舉寿弱。
下面講解基于注解的容器初始化犯眠。AnnotationConfigApplicationContext 提供了三個構造函數用于初始化容器。
· AnnotationConfigApplicationContext():該構造函數初始化一個空容器症革,容器不包含任何 Bean 信息筐咧,需要在稍后通過調用其 register() 方法注冊配置類,并調用 refresh() 方法刷新容器噪矛。
· AnnotationConfigApplicationContext(Class<?>... annotatedClasses):這是最常用的構造函數量蕊,通過將涉及到的配置類傳遞給該構造函數,以實現將相應配置類中的 Bean 自動注冊到容器中艇挨。
· AnnotationConfigApplicationContext(String... basePackages):該構造函數會自動掃描以給定的包及其子包下的所有類残炮,并自動識別所有的 Spring Bean,將其注冊到容器中缩滨。它不但識別標注 @Configuration 的配置類并正確解析势就,而且同樣能識別使用 @Repository、@Service脉漏、@Controller苞冯、@Component 標注的類。
除了使用上面第三種類型的構造函數讓容器自動掃描 Bean 的配置信息以外侧巨,AnnotationConfigApplicationContext 還提供了 scan() 方法舅锄,其功能與上面也類似,該方法主要用在容器初始化之后動態(tài)增加 Bean 至容器中刃泡。調用了該方法以后巧娱,通常需要立即手動調用 refresh() 刷新容器,以讓變更立即生效烘贴。
需要注意的是禁添,AnnotationConfigApplicationContext 在解析配置類時,會將配置類自身注冊為一個 Bean桨踪,因為 @Configuration 注解本身定義時被 @Component 標注了老翘。因此可以說,一個 @Configuration 同時也是一個 @Component。大多數情況下铺峭,開發(fā)者用不到該 Bean墓怀,并且在理想情況下,該 Bean 應該是對開發(fā)者透明的卫键。@Configuration 的定義如下所示:
[java] view plain copy
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
String value() default "";
}
在一般的項目中傀履,為了結構清晰,通常會根據軟件的模塊或者結構定義多個 XML 配置文件莉炉,然后再定義一個入口的配置文件钓账,該文件使用 <import/> 將其他的配置文件組織起來。最后只需將該文件傳給 ClassPathXmlApplicationContext 的構造函數即可絮宁。針對基于注解的配置梆暮,Spring 也提供了類似的功能,只需定義一個入口配置類绍昂,并在該類上使用 @Import 注解引入其他的配置類即可啦粹,最后只需要將該入口類傳遞給 AnnotationConfigApplicationContext。具體示例如下:
[java] view plain copy
@Configuration
@Import({BookStoreServiceConfig.class,BookStoreDaoConfig.class})
public class BookStoreConfig{ … }
10.****混合使用 XML 與注解進行 Bean 的配置:
設計 @Configuration 和 @Bean 的初衷窘游,并不是為了完全取代 XML唠椭,而是為了在 XML 之外多一種可行的選擇。由于 Spring 自發(fā)布以來张峰,Spring 開發(fā)小組便不斷簡化 XML 配置泪蔫,使得 XML 配置方式已經非常成熟,加上 Spring 2.0 以后出現了一系列命名空間的支持喘批,使得 XML 配置方式成為了使用簡單、功能強大的 Bean 定義方式铣揉。而且饶深,XML 配置的一些高級功能目前還沒有相關注解能夠直接支持。因此逛拱,在目前的多數項目中敌厘,要么使用純粹的 XML 配置方式進行 Bean 的配置,要么使用以注解為主朽合,XML 為輔的配置方式進行 Bean 的配置俱两。
之所以會出現兩者共存的情況,主要歸結為三個原因:其一曹步,目前絕大多數采用 Spring 進行開發(fā)的項目宪彩,幾乎都是基于 XML 配置方式的,Spring 在引入注解的同時讲婚,必須保證注解能夠與 XML 和諧共存尿孔,這是前提;其二,由于注解引入較晚活合,因此功能也沒有發(fā)展多年的 XML 強大雏婶,因此,對于復雜的配置白指,注解還很難獨當一面留晚,在一段時間內仍然需要 XML 的配合才能解決問題。除此之外告嘲,Spring 的 Bean 的配置方式與 Spring 核心模塊之間是解耦的错维,因此,改變配置方式對 Spring 的框架自身是透明的状蜗。Spring 可以通過使用 Bean 后處理器 (BeanPostProcessor) 非常方便的增加對于注解的支持需五。這在技術實現上非常容易的事情。
要使用混合配置方式轧坎,首先需要判斷以哪一種配置方式為主宏邮。對這個問題的不同回答將會直接影響到實現的方式。然而大可不必為此傷腦筋缸血,因為不論是以 XML 為主蜜氨,還是以注解為主,配置方式都是簡單而且容易理解的捎泻。這里不存在錯誤的決定飒炎,因為僅僅是表現方式不一樣。我們首先假設以 XML 配置為主的情況笆豁。
對于已經存在的大型項目郎汪,可能初期是以 XML 進行 Bean 配置的,后續(xù)逐漸加入了注解的支持闯狱,這時我們只需在 XML 配置文件中將被 @Configuration 標注的類定義為普通的 <bean>煞赢,同時注冊處理注解的 Bean 后處理器即可。示例如下:
[java] view plain copy
// 假設存在如下的 @Configuration 類:
package bookstore.config;
import bookstore.dao.*;
@Configuration
public class MyConfig{
@Bean
public UserDao userDao(){
**return** **new** UserDaoImpl();
}
}
此時哄孤,只需要在Spring XML配置文件中做如下聲明即可:
[xhtml] view plain copy
<beans … >
……
**<context:annotation-config** **/>**
**<bean** class=”demo.config.MyConfig”**/>**
</beans>
由于啟用了針對注解的 Bean 后處理器照筑,因此在 ApplicationContext 解析到 MyConfig 類時,會發(fā)現該類標注了 @Configuration 注解瘦陈,
隨后便會處理該類中標注 @Bean 的方法凝危,將這些方法的返回值注冊為容器總的 Bean。
對于以上的方式晨逝,如果存在多個標注了 @Configuration 的類蛾默,則需要在 XML 文件中逐一列出。另一種方式是使用前面提到的自動掃描功能
咏花,配置如下:
[xhtml] view plain copy
- <context:component-scan base-package=”bookstore.config” />
如此趴生,Spring 將掃描所有 demo.config 包及其子包中的類阀趴,識別所有標記了 @Component、@Controller苍匆、@Service刘急、@Repository
注解的類,由于 @Configuration 注解本身也用 @Component 標注了浸踩,Spring 將能夠識別出 @Configuration 標注類并正確解析之叔汁。
對于以注解為中心的配置方式,只需使用 @ImportResource 注解引入存在的 XML 即可检碗,如下所示:
[java] view plain copy
@Configuration
@ImportResource(“classpath:/bookstore/config/spring-beans.xml”)
public class MyConfig{
……
}
// 容器的初始化過程和純粹的以配置為中心的方式一致:
AnnotationConfigApplicationContext ctx =
**new** AnnotationConfigApplicationContext(MyConfig.**class**);
……