?
This document uses PHP Chinese website manual Release
到目前為止本章中的大多數(shù)例子都使用XML來(lái)指定配置元數(shù)據(jù),
這些元數(shù)據(jù)會(huì)生成Spring容器中的每個(gè)BeanDefinition
。
上一節(jié)(第?3.11?節(jié) “基于注解(Annotation-based)的配置”)演示了如何使用代碼級(jí)注解來(lái)提供大量配置元數(shù)據(jù)。
然而,即使是在那些例子中,“基礎(chǔ)”bean定義還是顯式地定義在XML文件中,注解只是用來(lái)驅(qū)動(dòng)依賴注入的。
本節(jié)中會(huì)介紹一種方法,通過(guò)掃描classpath并匹配過(guò)濾器來(lái)隱式地檢測(cè)候選組件 (candidate components)。
從Spring 2.0開始,引入了@Repository
注解,
用它來(lái)標(biāo)記充當(dāng)儲(chǔ)存庫(kù)(又稱 Data Access Object或DAO)角色或典型的類。利用這個(gè)標(biāo)記可以做很多事,
其中之一就是對(duì)第?12.6.4?節(jié) “異常轉(zhuǎn)化”中描述的異常進(jìn)行自動(dòng)轉(zhuǎn)換。
Spring 2.5引入了更多典型化注解(stereotype annotations):
@Component
、@Service
和
@Controller
。
@Component
是所有受Spring管理組件的通用形式;
而@Repository
、@Service
和
@Controller
則是@Component
的細(xì)化,
用來(lái)表示更具體的用例(例如,分別對(duì)應(yīng)了持久化層、服務(wù)層和表現(xiàn)層)。也就是說(shuō),
你能用@Component
來(lái)注解你的組件類,
但如果用@Repository
、@Service
或@Controller
來(lái)注解它們,你的類也許能更好地被工具處理,或與切面進(jìn)行關(guān)聯(lián)。
例如,這些典型化注解可以成為理想的切入點(diǎn)目標(biāo)。當(dāng)然,在Spring Framework以后的版本中,
@Repository
、@Service
和
@Controller
也許還能攜帶更多語(yǔ)義。如此一來(lái),如果你正在考慮服務(wù)層中是該用
@Component
還是@Service
,
那@Service
顯然是更好的選擇。同樣的,就像前面說(shuō)的那樣,
@Repository
已經(jīng)能在持久化層中進(jìn)行異常轉(zhuǎn)換時(shí)被作為標(biāo)記使用了。
Spring可以自動(dòng)檢測(cè)“被典型化”(stereotyped)的類,在ApplicationContext
中注冊(cè)相應(yīng)的BeanDefinition
。例如,下面的這兩個(gè)類就滿足這種自動(dòng)檢測(cè)的要求:
@Service public class SimpleMovieLister { private MovieFinder movieFinder; @Autowired public SimpleMovieLister(MovieFinder movieFinder) { this.movieFinder = movieFinder; } }
@Repository
public class JpaMovieFinder implements MovieFinder {
// implementation elided for clarity
}
要檢測(cè)這些類并注冊(cè)相應(yīng)的bean,需要在XML中包含以下元素,其中'basePackage'是兩個(gè)類的公共父包 (或者可以用逗號(hào)分隔的列表來(lái)分別指定包含各個(gè)類的包)。
<?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-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <context:component-scan base-package="org.example"/> </beans>
此外,在使用組件掃描元素時(shí),AutowiredAnnotationBeanPostProcessor
和CommonAnnotationBeanPostProcessor
會(huì)隱式地被包括進(jìn)來(lái)。
也就是說(shuō),連個(gè)組件都會(huì)被自動(dòng)檢測(cè)并織入 -
所有這一切都不需要在XML中提供任何bean配置元數(shù)據(jù)。
通過(guò)加入值為'false'的annotation-config屬性可以禁止注冊(cè)這些后置處理器。
默認(rèn)情況下,用@Component
、
@Repository
、@Service
或
@Controller
(或本身使用了@Component
注解的自定義注解)
注解的類是唯一會(huì)被檢測(cè)到的候選組件。但是可以很方便地通過(guò)自定義過(guò)濾器來(lái)改變并擴(kuò)展這一行為。
可以用'component-scan
'的include-filter或
exclude-filter子元素來(lái)進(jìn)行添加。
每個(gè)過(guò)濾器元素都要求有'type
'和'expression
'屬性。
下面給出了四個(gè)已有的可選過(guò)濾器。
表?3.7.?過(guò)濾器類型
過(guò)濾器類型 | 表達(dá)式范例 |
---|---|
annotation |
|
assignable |
|
regex | org\.example\.Default.* |
aspectj |
|
下面這個(gè)XML配置會(huì)忽略所有的@Repository
注解并用“stub”儲(chǔ)存庫(kù)代替。
<beans ...> <context:component-scan base-package="org.example"> <context:include-filter type="regex" expression=".*Stub.*Repository"/> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/> </context:component-scan> </beans>
你也可以用<component-scan/>元素的use-default-filters="false"
屬性來(lái)禁用默認(rèn)的過(guò)濾器。這會(huì)關(guān)閉對(duì)使用了@Component
、
@Repository
、@Service
或
@Controller
的類的自動(dòng)檢測(cè)。
當(dāng)一個(gè)組件在某個(gè)掃描過(guò)程中被自動(dòng)檢測(cè)到時(shí),會(huì)根據(jù)那個(gè)掃描器的BeanNameGenerator
策略生成它的bean名稱。默認(rèn)情況下,任何包含name
值的Spring“典型”注解
(@Component
、@Repository
、
@Service
和@Controller
)
會(huì)把那個(gè)名字提供給相關(guān)的bean定義。如果這個(gè)注解不包含name
值或是其他檢測(cè)到的組件
(比如被自定義過(guò)濾器發(fā)現(xiàn)的),默認(rèn)bean名稱生成器會(huì)返回小寫開頭的非限定(non-qualified)類名。
例如,如果發(fā)現(xiàn)了下面這兩個(gè)組件,它們的名字會(huì)是'myMovieLister'和'movieFinderImpl':
@Service("myMovieLister")
public class SimpleMovieLister {
// ...
}
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}
如果你不想使用默認(rèn)bean命名策略,可以提供一個(gè)自定義的命名策略。首先實(shí)現(xiàn)
BeanNameGenerator
接口,確認(rèn)包含了一個(gè)默認(rèn)的無(wú)參數(shù)構(gòu)造方法。然后在配置掃描器時(shí)提供一個(gè)全限定(fully-qualified)類名:
<beans ...> <context:component-scan base-package="org.example" name-generator="org.example.MyNameGenerator" /> </beans>
作為一條常規(guī),當(dāng)其他組件可能會(huì)顯式地引用一個(gè)組件時(shí)可以考慮用注解來(lái)指定名稱。 另一方面,當(dāng)容器負(fù)責(zé)織入時(shí),自動(dòng)生成的名稱就足夠了。
通常受Spring管理的組件,默認(rèn)或者最常用的作用域是“singleton”。然而,有時(shí)也會(huì)需要其他的作用域。
因此Spring 2.5還引入了一個(gè)新的@Scope
注解。只要在注解中提供作用域的名稱就行了,
比方說(shuō):
@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}
如果你想提供一個(gè)自定義的作用域解析策略,不使用基于注解的方法,實(shí)現(xiàn)ScopeMetadataResolver
接口,確認(rèn)包含一個(gè)默認(rèn)的沒有參數(shù)的構(gòu)造方法。然后在配置掃描器時(shí)提供全限定類名:
<beans ...> <context:component-scan base-package="org.example" scope-resolver="org.example.MyScopeResolver" /> </beans>
當(dāng)使用某些非singleton的作用域時(shí),可能需要為這些作用域中的對(duì)象生成代理。 原因在標(biāo)題為第?3.4.4.5?節(jié) “作用域bean與依賴”的章節(jié)中已經(jīng)說(shuō)過(guò)了。 為了這個(gè)目的,'component-scan'元素有一個(gè)scoped-proxy屬性。 三個(gè)可能值是:'no'、'interfaces'和'targetClass'。比方說(shuō),下面的配置會(huì)產(chǎn)生標(biāo)準(zhǔn)的JDK動(dòng)態(tài)代理:
<beans ...> <context:component-scan base-package="org.example" scoped-proxy="interfaces" /> </beans>
在名為第?3.11.2?節(jié) “基于注解的自動(dòng)連接微調(diào)”的章節(jié)里引入了@Qualifier
注解。
那節(jié)的例子中演示了@Qualifier
注解的用法,以及如何用自定義限定符注解在自動(dòng)織入解析時(shí)提供精細(xì)控制。
那些例子是基于XML bean定義的,所以限定符元數(shù)據(jù)是在XML中由'bean
'元素的
'qualifier
'或'meta
'子元素提供。使用classpath掃描來(lái)自動(dòng)檢測(cè)組件時(shí),
限定符元數(shù)據(jù)可以由候選類上的類別級(jí)(type-level)注解來(lái)提供。下面的三個(gè)例子就演示了這個(gè)技術(shù)。
@Component @Qualifier("Action") public class ActionMovieCatalog implements MovieCatalog { // ... }
@Component @Genre("Action") public class ActionMovieCatalog implements MovieCatalog { // ... }
@Component @Offline public class CachingMovieCatalog implements MovieCatalog { // ... }