自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

詳解Spring3基于Annotation的依賴(lài)注入實(shí)現(xiàn)

開(kāi)發(fā) 后端
本篇文章主要介紹Spring 的依賴(lài)配置方式與 Spring 框架的內(nèi)核自身是松耦合設(shè)計(jì)的,由于文字比較多,所以分為兩部分,希望大家諒解。

簡(jiǎn)介: Spring 的依賴(lài)配置方式與 Spring 框架的內(nèi)核自身是松耦合設(shè)計(jì)的。然而,直到 Spring 3.0 以前,使用 XML 進(jìn)行依賴(lài)配置幾乎是唯一的選擇。Spring 3.0 的出現(xiàn)改變了這一狀況,它提供了一系列的針對(duì)依賴(lài)注入的注解,這使得 Spring IoC 在 XML 文件之外多了一種可行的選擇。本文將詳細(xì)介紹如何使用這些注解進(jìn)行依賴(lài)配置的管理。

使用 @Repository、@Service、@Controller 和 @Component 將類(lèi)標(biāo)識(shí)為 Bean

Spring 自 2.0 版本開(kāi)始,陸續(xù)引入了一些注解用于簡(jiǎn)化 Spring 的開(kāi)發(fā)。@Repository 注解便屬于最先引入的一批,它用于將數(shù)據(jù)訪問(wèn)層 (DAO 層 ) 的類(lèi)標(biāo)識(shí)為 Spring Bean。具體只需將該注解標(biāo)注在 DAO 類(lèi)上即可。同時(shí),為了讓 Spring 能夠掃描類(lèi)路徑中的類(lèi)并識(shí)別出 @Repository 注解,需要在 XML 配置文件中啟用 Bean 的自動(dòng)掃描功能,這可以通過(guò) 實(shí)現(xiàn)。如下所示:

// 首先使用 @Repository 將 DAO 類(lèi)聲明為 Bean

  1. package bookstore.dao;   
  2. @Repository   
  3. public class UserDaoImpl implements UserDao{ …… }   

// 其次,在 XML 配置文件中啟動(dòng) Spring 的自動(dòng)掃描功能

  1. <beans … >   
  2.     ……   
  3. <context:component-scan base-package=”bookstore.dao” />   
  4. ……   
  5. beans>  

如此,我們就不再需要在 XML 中顯式使用 進(jìn)行 Bean 的配置。Spring 在容器初始化時(shí)將自動(dòng)掃描 base-package 指定的包及其子包下的所有 class 文件,所有標(biāo)注了 @Repository 的類(lèi)都將被注冊(cè)為 Spring Bean。

為什么 @Repository 只能標(biāo)注在 DAO 類(lèi)上呢?這是因?yàn)樵撟⒔獾淖饔貌恢皇菍㈩?lèi)識(shí)別為 Bean,同時(shí)它還能將所標(biāo)注的類(lèi)中拋出的數(shù)據(jù)訪問(wèn)異常封裝為 Spring 的數(shù)據(jù)訪問(wèn)異常類(lèi)型。 Spring 本身提供了一個(gè)豐富的并且是與具體的數(shù)據(jù)訪問(wèn)技術(shù)無(wú)關(guān)的數(shù)據(jù)訪問(wèn)異常結(jié)構(gòu),用于封裝不同的持久層框架拋出的異常,使得異常獨(dú)立于底層的框架。

Spring 2.5 在 @Repository 的基礎(chǔ)上增加了功能類(lèi)似的額外三個(gè)注解:@Component、@Service、@Constroller,它們分別用于軟件系統(tǒng)的不同層次:

♦ @Component 是一個(gè)泛化的概念,僅僅表示一個(gè)組件 (Bean) ,可以作用在任何層次。

♦ @Service 通常作用在業(yè)務(wù)層,但是目前該功能與 @Component 相同。

♦ @Constroller 通常作用在控制層,但是目前該功能與 @Component 相同。

通過(guò)在類(lèi)上使用 @Repository、@Component、@Service 和 @Constroller 注解,Spring 會(huì)自動(dòng)創(chuàng)建相應(yīng)的 BeanDefinition 對(duì)象,并注冊(cè)到 ApplicationContext 中。這些類(lèi)就成了 Spring 受管組件。這三個(gè)注解除了作用于不同軟件層次的類(lèi),其使用方式與 @Repository 是完全相同的。

另外,除了上面的四個(gè)注解外,用戶(hù)可以創(chuàng)建自定義的注解,然后在注解上標(biāo)注 @Component,那么,該自定義注解便具有了與所 @Component 相同的功能。不過(guò)這個(gè)功能并不常用。

當(dāng)一個(gè) Bean 被自動(dòng)檢測(cè)到時(shí),會(huì)根據(jù)那個(gè)掃描器的 BeanNameGenerator 策略生成它的 bean 名稱(chēng)。默認(rèn)情況下,對(duì)于包含 name 屬性的 @Component、@Repository、 @Service 和 @Controller,會(huì)把 name 取值作為 Bean 的名字。如果這個(gè)注解不包含 name 值或是其他被自定義過(guò)濾器發(fā)現(xiàn)的組件,默認(rèn) Bean 名稱(chēng)會(huì)是小寫(xiě)開(kāi)頭的非限定類(lèi)名。如果你不想使用默認(rèn) bean 命名策略,可以提供一個(gè)自定義的命名策略。首先實(shí)現(xiàn) BeanNameGenerator 接口,確認(rèn)包含了一個(gè)默認(rèn)的無(wú)參數(shù)構(gòu)造方法。然后在配置掃描器時(shí)提供一個(gè)全限定類(lèi)名,如下所示:

  1. <beans ...>   
  2. <context:component-scan   
  3.     base-package="a.b" name-generator="a.SimpleNameGenerator"/>   
  4. beans>  

與通過(guò) XML 配置的 Spring Bean 一樣,通過(guò)上述注解標(biāo)識(shí)的 Bean,其默認(rèn)作用域是"singleton",為了配合這四個(gè)注解,在標(biāo)注 Bean 的同時(shí)能夠指定 Bean 的作用域,Spring 2.5 引入了 @Scope 注解。使用該注解時(shí)只需提供作用域的名稱(chēng)就行了,如下所示:

  1. @Scope("prototype")   
  2. @Repository   
  3. public class Demo { … }  

如果你想提供一個(gè)自定義的作用域解析策略而不使用基于注解的方法,只需實(shí)現(xiàn) ScopeMetadataResolver 接口,確認(rèn)包含一個(gè)默認(rèn)的沒(méi)有參數(shù)的構(gòu)造方法。然后在配置掃描器時(shí)提供全限定類(lèi)名:

  1. package="a.b"     
  2. scope-resolver="footmark.SimpleScopeResolver" />   

使用 @PostConstruct 和 @PreDestroy 指定生命周期回調(diào)方法

Spring Bean 是受 Spring IoC 容器管理,由容器進(jìn)行初始化和銷(xiāo)毀的(prototype 類(lèi)型由容器初始化之后便不受容器管理),通常我們不需要關(guān)注容器對(duì) Bean 的初始化和銷(xiāo)毀操作,由 Spring 經(jīng)過(guò)構(gòu)造函數(shù)或者工廠方法創(chuàng)建的 Bean 就是已經(jīng)初始化完成并立即可用的。然而在某些情況下,可能需要我們手工做一些額外的初始化或者銷(xiāo)毀操作,這通常是針對(duì)一些資源的獲取和釋放操作。Spring 1.x 為此提供了兩種方式供用戶(hù)指定執(zhí)行生命周期回調(diào)的方法。

第一種方式是實(shí)現(xiàn) Spring 提供的兩個(gè)接口:InitializingBean 和 DisposableBean。如果希望在 Bean 初始化完成之后執(zhí)行一些自定義操作,則可以讓 Bean 實(shí)現(xiàn) InitializingBean 接口,該接口包含一個(gè) afterPropertiesSet() 方法,容器在為該 Bean 設(shè)置了屬性之后,將自動(dòng)調(diào)用該方法;如果 Bean 實(shí)現(xiàn)了 DisposableBean 接口,則容器在銷(xiāo)毀該 Bean 之前,將調(diào)用該接口的 destroy() 方法。這種方式的缺點(diǎn)是,讓 Bean 類(lèi)實(shí)現(xiàn) Spring 提供的接口,增加了代碼與 Spring 框架的耦合度,因此不推薦使用。

第二種方式是在 XML 文件中使用 的 init-method 和 destroy-method 屬性指定初始化之后和銷(xiāo)毀之前的回調(diào)方法,代碼無(wú)需實(shí)現(xiàn)任何接口。這兩個(gè)屬性的取值是相應(yīng) Bean 類(lèi)中的初始化和銷(xiāo)毀方法,方法名任意,但是方法不能有參數(shù)。示例如下:

  1. <bean id=”userService”   
  2. class=”bookstore.service.UserService”   
  3. init-method=”init” destroy-method=”destroy”>   
  4.     …   
  5. bean>  

Spring 2.5 在保留以上兩種方式的基礎(chǔ)上,提供了對(duì) JSR-250 的支持。JSR-250 規(guī)范定義了兩個(gè)用于指定聲明周期方法的注解:@PostConstruct 和 @PreDestroy。這兩個(gè)注解使用非常簡(jiǎn)單,只需分別將他們標(biāo)注于初始化之后執(zhí)行的回調(diào)方法或者銷(xiāo)毀之前執(zhí)行的回調(diào)方法上。由于使用了注解,因此需要配置相應(yīng)的 Bean 后處理器,亦即在 XML 中增加如下一行:

  1. <context:annotation-config />  

比較上述三種指定生命周期回調(diào)方法的方式,第一種是不建議使用的,不但其用法不如后兩種方式靈活,而且無(wú)形中增加了代碼與框架的耦合度。后面兩種方式開(kāi)發(fā)者可以根據(jù)使用習(xí)慣選擇其中一種,但是最好不要混合使用,以免增加維護(hù)的難度。

使用 @Required 進(jìn)行 Bean 的依賴(lài)檢查

依賴(lài)檢查的作用是,判斷給定 Bean 的相應(yīng) Setter 方法是否都在實(shí)例化的時(shí)候被調(diào)用了。而不是判斷字段是否已經(jīng)存在值了。Spring 進(jìn)行依賴(lài)檢查時(shí),只會(huì)判斷屬性是否使用了 Setter 注入。如果某個(gè)屬性沒(méi)有使用 Setter 注入,即使是通過(guò)構(gòu)造函數(shù)已經(jīng)為該屬性注入了值,Spring 仍然認(rèn)為它沒(méi)有執(zhí)行注入,從而拋出異常。另外,Spring 只管是否通過(guò) Setter 執(zhí)行了注入,而對(duì)注入的值卻沒(méi)有任何要求,即使注入的 ,Spring 也認(rèn)為是執(zhí)行了依賴(lài)注入。

標(biāo)簽提供了 dependency-check 屬性用于進(jìn)行依賴(lài)檢查。該屬性的取值包括以下幾種:

♦ none -- 默認(rèn)不執(zhí)行依賴(lài)檢查。可以在 標(biāo)簽上使用 default-dependency-check 屬性改變默認(rèn)值。

♦ simple -- 對(duì)原始基本類(lèi)型和集合類(lèi)型進(jìn)行檢查。

♦ objects -- 對(duì)復(fù)雜類(lèi)型進(jìn)行檢查(除了 simple 所檢查類(lèi)型之外的其他類(lèi)型)。

♦ all -- 對(duì)所有類(lèi)型進(jìn)行檢查。

舊版本使用 dependency-check 在配置文件中設(shè)置,缺點(diǎn)是粒度較粗。使用 Spring2.0 提供的 @Required 注解,提供了更細(xì)粒度的控制。@Required 注解只能標(biāo)注在 Setter 方法之上。因?yàn)橐蕾?lài)注入的本質(zhì)是檢查 Setter 方法是否被調(diào)用了,而不是真的去檢查屬性是否賦值了以及賦了什么樣的值。如果將該注解標(biāo)注在非 setXxxx() 類(lèi)型的方法則被忽略。

為了讓 Spring 能夠處理該注解,需要激活相應(yīng)的 Bean 后處理器。要激活該后處理器,只需在 XML 中增加如下一行即可。

  1. <context:annotation-config/>  

當(dāng)某個(gè)被標(biāo)注了 @Required 的 Setter 方法沒(méi)有被調(diào)用,則 Spring 在解析的時(shí)候會(huì)拋出異常,以提醒開(kāi)發(fā)者對(duì)相應(yīng)屬性進(jìn)行設(shè)置。

使用 @Resource、@Autowired 和 @Qualifier 指定 Bean 的自動(dòng)裝配策略

自動(dòng)裝配是指,Spring 在裝配 Bean 的時(shí)候,根據(jù)指定的自動(dòng)裝配規(guī)則,將某個(gè) Bean 所需要引用類(lèi)型的 Bean 注入進(jìn)來(lái)。 元素提供了一個(gè)指定自動(dòng)裝配類(lèi)型的 autowire 屬性,該屬性有如下選項(xiàng):

♦ no -- 顯式指定不使用自動(dòng)裝配。

♦ byName -- 如果存在一個(gè)和當(dāng)前屬性名字一致的 Bean,則使用該 Bean 進(jìn)行注入。如果名稱(chēng)匹配但是類(lèi)型不匹配,則拋出異常。如果沒(méi)有匹配的類(lèi)型,則什么也不做。

♦ byType -- 如果存在一個(gè)和當(dāng)前屬性類(lèi)型一致的 Bean ( 相同類(lèi)型或者子類(lèi)型 ),則使用該 Bean 進(jìn)行注入。byType 能夠識(shí)別工廠方法,即能夠識(shí)別 factory-method 的返回類(lèi)型。如果存在多個(gè)類(lèi)型一致的 Bean,則拋出異常。如果沒(méi)有匹配的類(lèi)型,則什么也不做。

♦ constructor -- 與 byType 類(lèi)似,只不過(guò)它是針對(duì)構(gòu)造函數(shù)注入而言的。如果當(dāng)前沒(méi)有與構(gòu)造函數(shù)的參數(shù)類(lèi)型匹配的 Bean,則拋出異常。使用該種裝配模式時(shí),優(yōu)先匹配參數(shù)最多的構(gòu)造函數(shù)。

♦ autodetect -- 根據(jù) Bean 的自省機(jī)制決定采用 byType 還是 constructor 進(jìn)行自動(dòng)裝配。如果 Bean 提供了默認(rèn)的構(gòu)造函數(shù),則采用 byType;否則采用 constructor 進(jìn)行自動(dòng)裝配。

當(dāng)使用 byType 或者 constructor 類(lèi)型的自動(dòng)裝配的時(shí)候,自動(dòng)裝配也支持引用類(lèi)型的數(shù)組或者使用了泛型的集合,這樣,Spring 就會(huì)檢查容器中所有類(lèi)型匹配的 Bean,組成集合或者數(shù)組后執(zhí)行注入。對(duì)于使用了泛型的 Map 類(lèi)型,如果鍵是 String 類(lèi)型,則 Spring 也會(huì)自動(dòng)執(zhí)行裝配,將所有類(lèi)型匹配的 Bean 作為值,Bean 的名字作為鍵。

我們可以給 增加 default-autowire 屬性,設(shè)置默認(rèn)的自動(dòng)封裝策略。默認(rèn)值為"no"。如果使用自動(dòng)裝配的同時(shí),也指定了 property 或者 constructor-arg 標(biāo)簽,則顯式指定的值將覆蓋自動(dòng)裝配的值。目前的自動(dòng)封裝不支持簡(jiǎn)單類(lèi)型,比如基本類(lèi)型、String、Class,以及它們的數(shù)組類(lèi)型。

在按類(lèi)型匹配的時(shí)候 ( 可能是 byType、constructor、autodetect),同一個(gè)類(lèi)型可能存在多個(gè) Bean,如果被注入的屬性是數(shù)組、集合或者 Map,這可能沒(méi)有問(wèn)題,但是如果只是簡(jiǎn)單的引用類(lèi)型,則會(huì)拋出異常。解決方法有如下幾種:

♦ 取消該 Bean 的自動(dòng)裝配特性,使用顯式的注入。我們可能不希望某個(gè) Bean 被當(dāng)作其他 Bean 執(zhí)行自動(dòng)封裝時(shí)的候選對(duì)象,我們可以給該 增加 autowire-candidate="false"。(autowire-candidate 屬性和 autowire 屬性相互獨(dú)立,互不相干。某個(gè) Bean 可以將 autowire-candidate 設(shè)置為 false,同時(shí)使用 autowire 特性。) 另外,我們可以設(shè)置 的 default-autowire-candidates 屬性,可以在該屬性中指定可以用于自動(dòng)裝配候選 Bean 的匹配模式,比如 default-autowire-candidates="*serv,*dao",這表示所有名字以 serv 或者 dao 結(jié)尾的 Bean 被列為候選,其他則忽略,相當(dāng)于其他 Bean 都指定為 autowire-candidate="false",此時(shí)可以顯式為 指定 autowire-candidate="true"。在 上指定的設(shè)置要覆蓋 上指定的設(shè)置。

♦ 如果在多個(gè)類(lèi)型相同的 Bean 中有首選的 Bean,那么可以將該 的 primary 屬性設(shè)置為 "true" ,這樣自動(dòng)裝配時(shí)便優(yōu)先使用該 Bean 進(jìn)行裝配。此時(shí)不能將 autowire-candidate 設(shè)為 false。

♦ 如果使用的是 Java 5 以上版本,可以使用注解進(jìn)行更細(xì)粒度的控制。

使用 @Autowired 和 @Qualifier 注解執(zhí)行自動(dòng)裝配

使用 @Autowired 注解進(jìn)行裝配,只能是根據(jù)類(lèi)型進(jìn)行匹配。@Autowired 注解可以用于 Setter 方法、構(gòu)造函數(shù)、字段,甚至普通方法,前提是方法必須有至少一個(gè)參數(shù)。@Autowired 可以用于數(shù)組和使用泛型的集合類(lèi)型。然后 Spring 會(huì)將容器中所有類(lèi)型符合的 Bean 注入進(jìn)來(lái)。@Autowired 標(biāo)注作用于 Map 類(lèi)型時(shí),如果 Map 的 key 為 String 類(lèi)型,則 Spring 會(huì)將容器中所有類(lèi)型符合 Map 的 value 對(duì)應(yīng)的類(lèi)型的 Bean 增加進(jìn)來(lái),用 Bean 的 id 或 name 作為 Map 的 key。

@Autowired 標(biāo)注作用于普通方法時(shí),會(huì)產(chǎn)生一個(gè)副作用,就是在容器初始化該 Bean 實(shí)例的時(shí)候就會(huì)調(diào)用該方法。當(dāng)然,前提是執(zhí)行了自動(dòng)裝配,對(duì)于不滿(mǎn)足裝配條件的情況,該方法也不會(huì)被執(zhí)行。

當(dāng)標(biāo)注了 @Autowired 后,自動(dòng)注入不能滿(mǎn)足,則會(huì)拋出異常。我們可以給 @Autowired 標(biāo)注增加一個(gè) required=false 屬性,以改變這個(gè)行為。另外,每一個(gè)類(lèi)中只能有一個(gè)構(gòu)造函數(shù)的 @Autowired.required() 屬性為 true。否則就出問(wèn)題了。如果用 @Autowired 同時(shí)標(biāo)注了多個(gè)構(gòu)造函數(shù),那么,Spring 將采用貪心算法匹配構(gòu)造函數(shù) ( 構(gòu)造函數(shù)最長(zhǎng) )。

@Autowired 還有一個(gè)作用就是,如果將其標(biāo)注在 BeanFactory 類(lèi)型、ApplicationContext 類(lèi)型、ResourceLoader 類(lèi)型、ApplicationEventPublisher 類(lèi)型、MessageSource 類(lèi)型上,那么 Spring 會(huì)自動(dòng)注入這些實(shí)現(xiàn)類(lèi)的實(shí)例,不需要額外的操作。

當(dāng)容器中存在多個(gè) Bean 的類(lèi)型與需要注入的相同時(shí),注入將不能執(zhí)行,我們可以給 @Autowired 增加一個(gè)候選值,做法是在 @Autowired 后面增加一個(gè) @Qualifier 標(biāo)注,提供一個(gè) String 類(lèi)型的值作為候選的 Bean 的名字。舉例如下:

  1. @Autowired(required=false)   
  2. @Qualifier("ppp")   
  3. public void setPerson(person p){}   

@Qualifier 甚至可以作用于方法的參數(shù) ( 對(duì)于方法只有一個(gè)參數(shù)的情況,我們可以將 @Qualifer 標(biāo)注放置在方法聲明上面,但是推薦放置在參數(shù)前面 ),舉例如下:

  1. @Autowired(required=false)   
  2. public void sayHello(@Qualifier("ppp")Person p,String name){}  

我們可以在配置文件中指定某個(gè) Bean 的 qualifier 名字,方法如下:

  1. <bean id="person" class="footmark.spring.Person">   
  2.     <qualifier value="ppp"/>   
  3. bean>  

如果沒(méi)有明確指定 Bean 的 qualifier 名字,那么默認(rèn)名字就是 Bean 的名字。通常,qualifier 應(yīng)該是有業(yè)務(wù)含義的,例如 "domain","persistent" 等,而不應(yīng)該是類(lèi)似 "person" 方式。

我們還可以將 @Qualifier 標(biāo)注在集合類(lèi)型上,那么所有 qualifier 名字與指定值相同的 Bean 都將被注入進(jìn)來(lái)。

最后,配置文件中需要指定每一個(gè)自定義注解的屬性值。我們可以使用 標(biāo)簽來(lái)代替 標(biāo)簽,如果 標(biāo)簽和 標(biāo)簽同時(shí)出現(xiàn),那么優(yōu)先使用 標(biāo)簽。如果沒(méi)有 標(biāo)簽,那么會(huì)用 提供的鍵值對(duì)來(lái)封裝 標(biāo)簽。示例如下:

  1. <bean class="footmark.HelloWorld">   
  2. <qualifier type="MovieQualifier">   
  3. <attribute key="format" value="VHS"/>   
  4. <attribute key="genre" value="Comedy"/>   
  5. qualifier>   
  6. bean>   
  7. <bean class="footmark.HelloWorld">   
  8. <meta key="format" value="DVD"/>   
  9. <meta key="genre" value="Action"/>   
  10. bean>   

@Autowired 注解對(duì)應(yīng)的后處理注冊(cè)與前面相似,只需在配置文件中增加如下一行即可:

  1. <context:annotation-config/>   

如果 @Autowired 注入的是 BeanFactory、ApplicationContext、ResourceLoader 等系統(tǒng)類(lèi)型,那么則不需要 @Qualifier,此時(shí)即使提供了 @Qualifier 注解,也將會(huì)被忽略;而對(duì)于自定義類(lèi)型的自動(dòng)裝配,如果使用了 @Qualifier 注解并且沒(méi)有名字與之匹配的 Bean,則自動(dòng)裝配匹配失敗。

#p#

使用 JSR-250 中的 @Resource 和 @Qualifier 注解

如果希望根據(jù) name 執(zhí)行自動(dòng)裝配,那么應(yīng)該使用 JSR-250 提供的 @Resource 注解,而不應(yīng)該使用 @Autowired 與 @Qualifier 的組合。

@Resource 使用 byName 的方式執(zhí)行自動(dòng)封裝。@Resource 標(biāo)注可以作用于帶一個(gè)參數(shù)的 Setter 方法、字段,以及帶一個(gè)參數(shù)的普通方法上。@Resource 注解有一個(gè) name 屬性,用于指定 Bean 在配置文件中對(duì)應(yīng)的名字。如果沒(méi)有指定 name 屬性,那么默認(rèn)值就是字段或者屬性的名字。@Resource 和 @Qualifier 的配合雖然仍然成立,但是 @Qualifier 對(duì)于 @Resource 而言,幾乎與 name 屬性等效。

如果 @Resource 沒(méi)有指定 name 屬性,那么使用 byName 匹配失敗后,會(huì)退而使用 byType 繼續(xù)匹配,如果再失敗,則拋出異常。在沒(méi)有為 @Resource 注解顯式指定 name 屬性的前提下,如果將其標(biāo)注在 BeanFactory 類(lèi)型、ApplicationContext 類(lèi)型、ResourceLoader 類(lèi)型、ApplicationEventPublisher 類(lèi)型、MessageSource 類(lèi)型上,那么 Spring 會(huì)自動(dòng)注入這些實(shí)現(xiàn)類(lèi)的實(shí)例,不需要額外的操作。此時(shí) name 屬性不需要指定 ( 或者指定為""),否則注入失敗;如果使用了 @Qualifier,則該注解將被忽略。而對(duì)于用戶(hù)自定義類(lèi)型的注入,@Qualifier 和 name 等價(jià),并且不被忽略。

的 primary 和 autowire-candidate 屬性對(duì) @Resource、@Autowired 仍然有效。

使用 @Configuration 和 @Bean 進(jìn)行 Bean 的聲明

雖然 2.0 版本發(fā)布以來(lái),Spring 陸續(xù)提供了十多個(gè)注解,但是提供的這些注解只是為了在某些情況下簡(jiǎn)化 XML 的配置,并非要取代 XML 配置方式。這一點(diǎn)可以從 Spring IoC 容器的初始化類(lèi)可以看出:ApplicationContext 接口的最常用的實(shí)現(xiàn)類(lèi)是 ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext,以及面向 Portlet 的 XmlPortletApplicationContext 和面向 web 的 XmlWebApplicationContext,它們都是面向 XML 的。Spring 3.0 新增了另外兩個(gè)實(shí)現(xiàn)類(lèi):AnnotationConfigApplicationContext 和 AnnotationConfigWebApplicationContext。從名字便可以看出,它們是為注解而生,直接依賴(lài)于注解作為容器配置信息來(lái)源的 IoC 容器初始化類(lèi)。由于 AnnotationConfigWebApplicationContext 是 AnnotationConfigApplicationContext 的 web 版本,其用法與后者相比幾乎沒(méi)有什么差別,因此本文將以 AnnotationConfigApplicationContext 為例進(jìn)行講解。

AnnotationConfigApplicationContext 搭配上 @Configuration 和 @Bean 注解,自此,XML 配置方式不再是 Spring IoC 容器的唯一配置方式。兩者在一定范圍內(nèi)存在著競(jìng)爭(zhēng)的關(guān)系,但是它們?cè)诖蠖鄶?shù)情況下還是相互協(xié)作的關(guān)系,兩者的結(jié)合使得 Spring IoC 容器的配置更簡(jiǎn)單,更強(qiáng)大。

之前,我們將配置信息集中寫(xiě)在 XML 中,如今使用注解,配置信息的載體由 XML 文件轉(zhuǎn)移到了 Java 類(lèi)中。我們通常將用于存放配置信息的類(lèi)的類(lèi)名以 “Config” 結(jié)尾,比如 AppDaoConfig.java、AppServiceConfig.java 等等。我們需要在用于指定配置信息的類(lèi)上加上 @Configuration 注解,以明確指出該類(lèi)是 Bean 配置的信息源。并且 Spring 對(duì)標(biāo)注 Configuration 的類(lèi)有如下要求:

♦ 配置類(lèi)不能是 final 的;

♦ 配置類(lèi)不能是本地化的,亦即不能將配置類(lèi)定義在其他類(lèi)的方法內(nèi)部;

♦ 配置類(lèi)必須有一個(gè)無(wú)參構(gòu)造函數(shù)。

AnnotationConfigApplicationContext 將配置類(lèi)中標(biāo)注了 @Bean 的方法的返回值識(shí)別為 Spring Bean,并注冊(cè)到容器中,受 IoC 容器管理。@Bean 的作用等價(jià)于 XML 配置中的 標(biāo)簽。示例如下:

  1. @Configuration   
  2. public class BookStoreDaoConfig{   
  3.     @Bean   
  4.     public UserDao userDao(){ return new UserDaoImpl();}   
  5.     @Bean   
  6.     public BookDao bookDao(){return new BookDaoImpl();}   
  7. }   

Spring 在解析到以上文件時(shí),將識(shí)別出標(biāo)注 @Bean 的所有方法,執(zhí)行之,并將方法的返回值 ( 這里是 UserDaoImpl 和 BookDaoImpl 對(duì)象 ) 注冊(cè)到 IoC 容器中。默認(rèn)情況下,Bean 的名字即為方法名。因此,與以上配置等價(jià)的 XML 配置如下:

  1. <bean id=”userDao” class=”bookstore.dao.UserDaoImpl”/>    
  2.  <bean id=”bookDao” class=”bookstore.dao.BookDaoImpl”/>       

    

@Bean 具有以下四個(gè)屬性:

♦ name -- 指定一個(gè)或者多個(gè) Bean 的名字。這等價(jià)于 XML 配置中 的 name 屬性。

♦ initMethod -- 容器在初始化完 Bean 之后,會(huì)調(diào)用該屬性指定的方法。這等價(jià)于 XML 配置中 的 init-method 屬性。

♦ destroyMethod -- 該屬性與 initMethod 功能相似,在容器銷(xiāo)毀 Bean 之前,會(huì)調(diào)用該屬性指定的方法。這等價(jià)于 XML 配置中 的 destroy-method 屬性。

♦ autowire -- 指定 Bean 屬性的自動(dòng)裝配策略,取值是 Autowire 類(lèi)型的三個(gè)靜態(tài)屬性。Autowire.BY_NAME,Autowire.BY_TYPE,Autowire.NO。與 XML 配置中的 autowire 屬性的取值相比,這里少了 constructor,這是因?yàn)?constructor 在這里已經(jīng)沒(méi)有意義了。

@Bean 沒(méi)有直接提供指定作用域的屬性,可以通過(guò) @Scope 來(lái)實(shí)現(xiàn)該功能,關(guān)于 @Scope 的用法已在上文列舉。

下面講解基于注解的容器初始化。AnnotationConfigApplicationContext 提供了三個(gè)構(gòu)造函數(shù)用于初始化容器。

♦ AnnotationConfigApplicationContext():該構(gòu)造函數(shù)初始化一個(gè)空容器,容器不包含任何 Bean 信息,需要在稍后通過(guò)調(diào)用其 register() 方法注冊(cè)配置類(lèi),并調(diào)用 refresh() 方法刷新容器。

♦ AnnotationConfigApplicationContext(Class... annotatedClasses):這是最常用的構(gòu)造函數(shù),通過(guò)將涉及到的配置類(lèi)傳遞給該構(gòu)造函數(shù),以實(shí)現(xiàn)將相應(yīng)配置類(lèi)中的 Bean 自動(dòng)注冊(cè)到容器中。

♦ AnnotationConfigApplicationContext(String... basePackages):該構(gòu)造函數(shù)會(huì)自動(dòng)掃描以給定的包及其子包下的所有類(lèi),并自動(dòng)識(shí)別所有的 Spring Bean,將其注冊(cè)到容器中。它不但識(shí)別標(biāo)注 @Configuration 的配置類(lèi)并正確解析,而且同樣能識(shí)別使用 @Repository、@Service、@Controller、@Component 標(biāo)注的類(lèi)。

除了使用上面第三種類(lèi)型的構(gòu)造函數(shù)讓容器自動(dòng)掃描 Bean 的配置信息以外,AnnotationConfigApplicationContext 還提供了 scan() 方法,其功能與上面也類(lèi)似,該方法主要用在容器初始化之后動(dòng)態(tài)增加 Bean 至容器中。調(diào)用了該方法以后,通常需要立即手動(dòng)調(diào)用 refresh() 刷新容器,以讓變更立即生效。

需要注意的是,AnnotationConfigApplicationContext 在解析配置類(lèi)時(shí),會(huì)將配置類(lèi)自身注冊(cè)為一個(gè) Bean,因?yàn)?@Configuration 注解本身定義時(shí)被 @Component 標(biāo)注了。因此可以說(shuō),一個(gè) @Configuration 同時(shí)也是一個(gè) @Component。大多數(shù)情況下,開(kāi)發(fā)者用不到該 Bean,并且在理想情況下,該 Bean 應(yīng)該是對(duì)開(kāi)發(fā)者透明的。@Configuration 的定義如下所示:

  1. @Target({ElementType.TYPE})   
  2. @Retention(RetentionPolicy.RUNTIME)   
  3. @Documented   
  4. @Component   
  5. public @interface Configuration {   
  6. String value() default "";   
  7. }   

在一般的項(xiàng)目中,為了結(jié)構(gòu)清晰,通常會(huì)根據(jù)軟件的模塊或者結(jié)構(gòu)定義多個(gè) XML 配置文件,然后再定義一個(gè)入口的配置文件,該文件使用 將其他的配置文件組織起來(lái)。最后只需將該文件傳給 ClassPathXmlApplicationContext 的構(gòu)造函數(shù)即可。針對(duì)基于注解的配置,Spring 也提供了類(lèi)似的功能,只需定義一個(gè)入口配置類(lèi),并在該類(lèi)上使用 @Import 注解引入其他的配置類(lèi)即可,最后只需要將該入口類(lèi)傳遞給 AnnotationConfigApplicationContext。具體示例如下:

  1. @Configuration   
  2. @Import({BookStoreServiceConfig.class,BookStoreDaoConfig.class})   
  3. public class BookStoreConfig{ … }  

混合使用 XML 與注解進(jìn)行 Bean 的配置

設(shè)計(jì) @Configuration 和 @Bean 的初衷,并不是為了完全取代 XML,而是為了在 XML 之外多一種可行的選擇。由于 Spring 自發(fā)布以來(lái),Spring 開(kāi)發(fā)小組便不斷簡(jiǎn)化 XML 配置,使得 XML 配置方式已經(jīng)非常成熟,加上 Spring 2.0 以后出現(xiàn)了一系列命名空間的支持,使得 XML 配置方式成為了使用簡(jiǎn)單、功能強(qiáng)大的 Bean 定義方式。而且,XML 配置的一些高級(jí)功能目前還沒(méi)有相關(guān)注解能夠直接支持。因此,在目前的多數(shù)項(xiàng)目中,要么使用純粹的 XML 配置方式進(jìn)行 Bean 的配置,要么使用以注解為主,XML 為輔的配置方式進(jìn)行 Bean 的配置。

之所以會(huì)出現(xiàn)兩者共存的情況,主要?dú)w結(jié)為三個(gè)原因:其一,目前絕大多數(shù)采用 Spring 進(jìn)行開(kāi)發(fā)的項(xiàng)目,幾乎都是基于 XML 配置方式的,Spring 在引入注解的同時(shí),必須保證注解能夠與 XML 和諧共存,這是前提;其二,由于注解引入較晚,因此功能也沒(méi)有發(fā)展多年的 XML 強(qiáng)大,因此,對(duì)于復(fù)雜的配置,注解還很難獨(dú)當(dāng)一面,在一段時(shí)間內(nèi)仍然需要 XML 的配合才能解決問(wèn)題。除此之外,Spring 的 Bean 的配置方式與 Spring 核心模塊之間是解耦的,因此,改變配置方式對(duì) Spring 的框架自身是透明的。Spring 可以通過(guò)使用 Bean 后處理器 (BeanPostProcessor) 非常方便的增加對(duì)于注解的支持。這在技術(shù)實(shí)現(xiàn)上非常容易的事情。

要使用混合配置方式,首先需要判斷以哪一種配置方式為主。對(duì)這個(gè)問(wèn)題的不同回答將會(huì)直接影響到實(shí)現(xiàn)的方式。然而大可不必為此傷腦筋,因?yàn)椴徽撌且?XML 為主,還是以注解為主,配置方式都是簡(jiǎn)單而且容易理解的。這里不存在錯(cuò)誤的決定,因?yàn)閮H僅是表現(xiàn)方式不一樣。我們首先假設(shè)以 XML 配置為主的情況。

對(duì)于已經(jīng)存在的大型項(xiàng)目,可能初期是以 XML 進(jìn)行 Bean 配置的,后續(xù)逐漸加入了注解的支持,這時(shí)我們只需在 XML 配置文件中將被 @Configuration 標(biāo)注的類(lèi)定義為普通的 ,同時(shí)注冊(cè)處理注解的 Bean 后處理器即可。示例如下:

// 假設(shè)存在如下的 @Configuration 類(lèi):

  1. package bookstore.config;   
  2. import bookstore.dao.*;   
  3. @Configuration   
  4. public class MyConfig{   
  5. @Bean   
  6.     public UserDao userDao(){   
  7.         return new UserDaoImpl();   
  8.     }   
  9. }  

此時(shí),只需在 XML 中作如下聲明即可:

  1. <beans … >   
  2.     ……   
  3.     <context:annotation-config />   
  4.     <bean class=”demo.config.MyConfig”/>   
  5. beans>  

由于啟用了針對(duì)注解的 Bean 后處理器,因此在 ApplicationContext 解析到 MyConfig 類(lèi)時(shí),會(huì)發(fā)現(xiàn)該類(lèi)標(biāo)注了 @Configuration 注解,隨后便會(huì)處理該類(lèi)中標(biāo)注 @Bean 的方法,將這些方法的返回值注冊(cè)為容器總的 Bean。

對(duì)于以上的方式,如果存在多個(gè)標(biāo)注了 @Configuration 的類(lèi),則需要在 XML 文件中逐一列出。另一種方式是使用前面提到的自動(dòng)掃描功能,配置如下:

  1. <context:component-scan base-package=”bookstore.config” />  

如此,Spring 將掃描所有 demo.config 包及其子包中的類(lèi),識(shí)別所有標(biāo)記了 @Component、@Controller、@Service、@Repository 注解的類(lèi),由于 @Configuration 注解本身也用 @Component 標(biāo)注了,Spring 將能夠識(shí)別出 @Configuration 標(biāo)注類(lèi)并正確解析之。

對(duì)于以注解為中心的配置方式,只需使用 @ImportResource 注解引入存在的 XML 即可,如下所示:

  1. @Configuration   
  2. @ImportResource(“classpath:/bookstore/config/spring-beans.xml”)   
  3. public class MyConfig{   
  4. ……   
  5. }   

// 容器的初始化過(guò)程和純粹的以配置為中心的方式一致:

  1. AnnotationConfigApplicationContext ctx =   
  2.               new AnnotationConfigApplicationContext(MyConfig.class);   
  3. ……  

結(jié)束語(yǔ)

從 2.0 版本開(kāi)始,Spring 的每一次更新都會(huì)提供更多新的注解供開(kāi)發(fā)者使用。這滿(mǎn)足了注解愛(ài)好者的胃口。但是正如前面所說(shuō),Spring 提供更多的注解并不是為了有朝一日取代 XML 配置方式,而是為了給開(kāi)發(fā)者多一種選擇。兩種聲明 Bean 的方式各有特色,XML 方式更加靈活,并且發(fā)展的相對(duì)成熟,這種配置方式為大多數(shù) Spring 開(kāi)發(fā)者熟悉;注解方式使用起來(lái)非常簡(jiǎn)潔,但是尚處于發(fā)展階段。我們很難說(shuō)兩種配置方式孰優(yōu)孰劣,但是如果能夠靈活搭配兩種方式,一定能夠進(jìn)一步提升開(kāi)發(fā)效率。



【編輯推薦】

  1. Java EE進(jìn)階之Spring事務(wù)深入淺出
  2. Spring事務(wù)管理高級(jí)應(yīng)用難點(diǎn)剖析
  3. Spring-MVC入門(mén)(一):入門(mén)實(shí)例
  4. 多圖詳解Spring框架的設(shè)計(jì)理念與設(shè)計(jì)模式
責(zé)任編輯:金賀 來(lái)源: JavaEye博客
相關(guān)推薦

2011-05-31 10:00:21

Android Spring 依賴(lài)注入

2015-09-02 11:22:36

JavaScript實(shí)現(xiàn)思路

2022-04-30 08:50:11

控制反轉(zhuǎn)Spring依賴(lài)注入

2017-08-16 16:00:05

PHPcontainer依賴(lài)注入

2016-03-21 17:08:54

Java Spring注解區(qū)別

2011-04-15 09:44:45

Spring

2024-11-27 00:24:04

2009-09-08 15:22:20

Spring依賴(lài)注入

2020-08-06 00:14:16

Spring IoC依賴(lài)注入開(kāi)發(fā)

2010-10-14 09:05:36

ASP.NET MVC

2022-12-29 08:54:53

依賴(lài)注入JavaScript

2023-10-07 08:35:07

依賴(lài)注入Spring

2018-03-12 10:02:30

PHP依賴(lài)注入

2020-02-10 15:50:18

Spring循環(huán)依賴(lài)Java

2012-07-22 20:34:27

springMVCJUnit

2025-02-17 00:00:55

NET開(kāi)發(fā)依賴(lài)注入

2020-11-02 07:00:29

Spring Boo注解自動(dòng)化

2021-07-29 06:55:03

Spring@AutowriedbyType注入

2024-08-27 11:00:56

單例池緩存bean

2024-08-26 08:52:41

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)