1. 程式人生 > >[Spring實戰系列](13)使用註解自動裝配

[Spring實戰系列](13)使用註解自動裝配

1. 簡介

從Spring2.5開始,我們就可以使用註解的自動裝配方式裝配Spring Bean的屬性。使用註解自動裝配方式與在XML中使用autowire屬性自動裝配沒有太大區別。那為啥還要研發出這樣一種裝配方式?肯定有它獨特的地方:使用註解自動裝配方式允許更細粒度的自動裝配,我們可以選擇性的標註某一個屬性對其應用自動裝配

2. 啟用註解裝配

Spring容器預設禁用註解裝配。所以,在使用基於註解的自動裝配前,我們需要在Spring配置中啟用它。最簡單的啟用方式是使用Spring的context名稱空間配置的<context:annotation-config>元素<?
xml version="1.0" encoding="UTF-8"?>
<beansxmlns="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.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><context:annotation-config/></beans> <context:annotation-config/>元素告訴Spring我們打算使用基於註解的自動裝配。一旦裝配完成,我們就可以對程式碼添加註解,表示Spring應該為屬性,方法和構造器進行自動裝配。

3. 使用@Autowired

@Autowiredpublic
void setSchool(School school){
this.school = school;} 當我們對setSchool()方法使用@Autowired註解時,我們可以移除用來定義Student的school屬性所對應的<property>元素了。如下面配置檔案中的yoona Bean。
<?xml version="1.0" encoding="UTF-8"?><beansxmlns="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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><context:annotation-config/><beanid="yoona"class="com.sjf.bean.Student"><propertyname="name"value="yoona"/><propertyname="age"value="24"/></bean><beanid="xidianSchool"class="com.sjf.bean.School"><propertyname="name"value="西安電子科技大學"/><propertyname="location"value="西安"/></bean></beans> 執行結果:

name:yoona   age:24   school:西安電子科技大學[西安]
在這個例項中,我們對Student的school屬性使用使用@Autowired註解,name屬性和age屬性使用setter注入方式。當Spring發現我們對setSchool()方法使用@Autowired註解時,Spring就會嘗試對該方法執行byType自動裝配。查詢到只有一個Bean(xidianSchool)滿足其條件,自動把xidianSchool Bean注入到Student的school屬性中。 注意點:

使用了@Autowired註解時,Spring就會嘗試對該方法執行byType自動裝配
特別之處(不同XML方式):

使用@Autowired註解不只可以使用它標註setter方法,還可以標註需要自動裝配Bean引用的任意方法。甚至是構造器,@Autowired註解表示當建立Bean時,即使Spring XML檔案中沒有使用<constructor-arg>元素配置Bean,該構造器也需要進行自動裝配。Spring會從所有滿足裝配條件的構造器中選擇入參最多的那個構造器。還可以標註屬性,並可以刪除setter方法
(1)使用@Autowired註解標註setter方法: @Autowiredpublicvoid setSchool(School school){this.school = school;} (2)使用@Autowired註解標註屬性(不受限於private關鍵字): @AutowiredprivateSchool school; (3)使用@Autowired註解標註任意方法:
@Autowiredpublicvoid assemblySchool(School school){this.school = school;} (4)使用@Autowired註解標註構造器:
@AutowiredpublicStudent(School school){this.school = school;} 侷限性(同於XML方式):

應用中必須只能有一個Bean適合裝配到@Autowired註解所標註的屬性或者引數中。如果沒有匹配的Bean,或者存在多個匹配的Bean,@Autowired註解就會遇到一些麻煩。
3.1 沒有匹配的Bean
如果沒有匹配的Bean,@Autowired註解就會失敗,丟擲NoSuchBeanDefinitionException異常
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException No qualifying bean of type [com.sjf.bean.School] found for dependency: expected  at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}     at org.springframework.beans.factory.support.DefaultListableBeanFactory. raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1373)     at org.springframework.beans.factory.support.DefaultListableBeanFactory. doResolveDependency(DefaultListableBeanFactory.java:1119)     at org.springframework.beans.factory.support.DefaultListableBeanFactory. resolveDependency(DefaultListableBeanFactory.java:1014)     at org.springframework.beans.factory.annotation. AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:618)     ... 15 more  
屬性不一定非要裝配,null值也是可以接受的。這種情況下,我們可以設定@Autowired的required屬性為false來裝配自動裝配的是可選的。 @Autowired(required =false)publicvoid setSchool(School school){this.school = school;} 在這裡,Spring將嘗試裝配school屬性,但是如果沒有查詢到與之匹配的型別為School的Bean,不會丟擲任何異常,並且Student Bean的school屬性會設定為null。 注意點:

required屬性可以用於@Autowired註解所使用的任意地方。但是當使用構造器裝配時,只有一個構造器可以將@Autowired的required屬性設定true其他使用@Autowired註解所標註的構造器只能將required屬性設定為false
3.2 多個匹配的Bean
另一個問題是可能會有足夠多的(至少2個)都滿足裝配條件,並且都可以被裝配到屬性或引數中。假如,我們有兩個Bean是School型別的,滿足裝配條件。這種情況下,@Autowired註解沒有辦法選擇哪一個Bean才是它需要的,會丟擲NoUniqueBeanDefinitionException異常。 <?xml version="1.0" encoding="UTF-8"?><beansxmlns="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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><context:annotation-config/><beanid="yoona"class="com.sjf.bean.Student"><propertyname="name"value="yoona"/><propertyname="age"value="24"/></bean><beanid="xidianSchool"class="com.sjf.bean.School"><propertyname="name"value="西安電子科技大學"/><propertyname="location"value="西安"/></bean><beanid="shandaSchool"class="com.sjf.bean.School"><propertyname="name"value="山東大學"/><propertyname="location"value="山東"/></bean></beans> 丟擲異常:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException No qualifying bean of type [com.sjf.bean.School] is defined: expected single matching bean but found 2: xidianSchool,shandaSchool   at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1126)   at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014)   at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement. inject(AutowiredAnnotationBeanPostProcessor.java:618)   ... 15 more
為了幫助@Autowired鑑別出哪一個Bean才是我們所需要的,我們可以配合使用Spring的@Qualifier註解。 @Autowired@Qualifier("shandaSchool")publicvoid setSchool(School school){this.school = school;} 這樣,我們就使用@Qualifier註解嘗試注入ID為"shandaSchool"的Bean。表面上看,把@Autowired的byType自動裝配轉換為顯示的byName裝配。實際只是使用@Qualifier註解縮小了自動裝配挑選候選Bean的範圍。上例中通過指定Bean 的ID把選擇範圍縮小到只剩一個Bean。 除了通過Bean的ID縮小選擇範圍,還可以通過在Bean上直接使用qualifier來縮小範圍。 <?xml version="1.0" encoding="UTF-8"?><beansxmlns="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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><context:annotation-config/><beanid="yoona"class="com.sjf.bean.Student"><propertyname="name"value="yoona"/><propertyname="age"value="24"/></bean><beanid="xidianSchool"class="com.sjf.bean.School"><propertyname="name"value="西安電子科技大學"/><propertyname="location"value="西安"/><qualifiervalue="ShanXi"/></bean><beanid="shandaSchool"class="com.sjf.bean.School"><propertyname="name"value="山東大學"/><propertyname="location"value="山東"/><qualifiervalue="ShanDong"/></bean>