文檔地址: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#beans-classpath-scanning 本章中大部分例子是用xml來指定配置元數據,以生成容器裏的每一個bean定義.上個部分描述瞭如何經過資源註解來提供大量的配置元數據.可是,這不少例子中,不少bean定義是在xml文件中完成的,而註解只負責依賴注入.本節描述如何經過掃描路徑來檢測可選組件.候選組件都是一些能匹配一些條件的類或者在容器中註冊了相應的bean定義.這移除了使用xml註冊bean的必要,所以你可用使用註解,AspectJ類型表達,又或者你的自定義攔截條件 來選擇容器中註冊的bean定義.html
從spring3.0,spring的java配置項目提供了的不少功能已是spring核心功能的一部分.這容許你使用java而不是xml文件來定義beans.學習@Configuration,@Bean,@Import,@Dependson註解,並學習如何使用.java
@Repository註解是一個任何能完成repository(也就是DAO)角色或模板的類的標誌.使用這些標誌能夠自動轉譯異常,如Section 20.2.2,"Exception translation";web
spring提供了不少固定註解:@Component,@Service,@Controller,@Repository.@Component是一個spring管理組件的基本模板類型.@Repository,@Service,@Controller是對@Componet的特殊化,應用於特定的場合,例如,他們分別在持久化,服務層,表現層中使用.所以,你能夠用@Component來標誌你的組件,但你最好用@Repository,@Service,@Controller來替換,由於這樣可使這些類更好的而配合工做操做或者和切面協同.例如,這些模板註解都是切入點的理想目標.spring框架在之後的發佈版中@Repository,@Service,@Controller可能會攜帶額外的語法.所以,當你考慮用@Service或@Component來標註你的服務層是,@Service是更好的選擇.同上文所示,@Repository已被當爲了一個支持持久層自動異常轉譯的標誌.算法
不少spring提供的註解能夠做爲元註解在你的代碼裏使用.元註解是指能夠在其餘註解中使用的註解.例如,上文中提到的@Service註解中就有@Component元註解.spring
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Component // Spring will see this and treat @Service in the same way as @Component public @interface Service { / / .... }
元註解能夠被聚合生成組合註解.例如,spring mvc的@RestController註解就是@Controller和@ResponseBody組合而成的.express
另外,組合註解能夠從新申明元註解的屬性來容許用戶自定義.當你只打算暴露一部分元註解屬性時這將很是有用.例如,spring的@SessionScope以硬編碼的方式控制scope的名字爲session,但扔容許自定義proxyMode的值.安全
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Scope(WebApplicationContext.SCOPE_SESSION) public @interface SessionScope { /** * Alias for {@link Scope#proxyMode}. * <p>Defaults to {@link ScopedProxyMode#TARGET_CLASS}. */ @AliasFor(annotation = Scope.class) ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS; }
@SessionScope不申明proxyMode屬性仍能夠以下使用session
@Service @SessionScope(proxyMode = ScopedProxyMode.INTERFACES) public class SessionScopedUserService implements UserService { // ... }
更多細節,請查看spring annotaion programming Modelmvc
spring 能夠自動檢測模板類並經過ApplicationContext來註冊相應的bean定義.例如,一下兩個類將會代理以實現自動檢測.app
@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 }
要自動檢測這些類並註冊相應的bean,你須要在你的@Configuration類中添加@ComponentScan註解,@ComponentScan中的basePackages屬性應該是兩個類的共同父包.(另外,你能夠制定一個逗號,分號,空白分割的列表來包含每一個類的父包);
@Configuration @ComponentScan(basePackages = "org.example") public class AppConfig { ... }
爲了簡化,上面的能夠直接使用註解你的value屬性,如 @ComponentScan("org.example")
如下是相應的xml
<?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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="org.example"/> </beans>
context:component-scan的隱式的包含了context:annotation-cofig的功能,因此你在使用context:component-scan時不須要使用context:annotation-config;
類路徑包的掃描須要相應的目錄實體在類路徑中真實存在.當你用Ant構建JARS時,要保證你沒有開啓你的文件轉化jar包任務.另外,在一些環境裏,依據安全策略類路徑目錄將沒法暴露.例如,jdk1.7中單獨的應用.
還有,當你使用component-scan元素時,AutowiredAnnotaionBeanPostProcessor,CommonAnnotationBeanPostProcessor都是隱式包含的.這意味着這兩個組件會自動檢測被一塊兒註冊,不須要在xml裏提供任何bean配置元數據.
另,你也能夠不註冊AutowiredAnnotaionBeanPostProcessor,CommonAnnotationBeanPostProcessor,這須要你在annotation-config元素中將值設爲false;
通常而言,用@Component,@Repository,@Service,,@Controller,或其餘自己標記了@Component註解的註解才能被檢測爲可選組件.可是,你能夠簡單的經過使用自定義過濾器來修改並擴展這個行爲.將他們做爲includeFilters或excludeFilters的參數添加到@ComponentScan註解裏.下面的表將描述攔截選項.
annotaion,assignable,aspectj,regex,custom五種. 下面的例子代表全部的@Repository註解都會被排除,並使用以stub結尾的repository替代.
@Configuration @ComponentScan(basePackages = "org.example", includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"), excludeFilters = @Filter(Repository.class)) public class AppConfig { ... }
等同於
<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>
@Filter,屬性時type,pattern,默認是annotation;你也能夠經過設置@ComponentScan的useDefaultFileters=false或設置<component-scan>元素中的use-default-filters="false"來禁用默認過濾器.而受影響的類是標註了@Componnet,@Repository,@Service,@Controller,或者@Configuration的類.
spring 的components也能夠向容器提供bean定義.你能夠像在@Configuration註解類中同樣使用@Bean註解來定義bean的元數據.下面是一個簡單的例子:
@Component public class FactoryMethodComponent { @Bean @Qualifier("public") public TestBean publicInstance() { return new TestBean("publicInstance"); } public void doWork() { // Component method implementation omitted } }
這個類是一個spring組件類,在它的doWork()方法裏有應用特定的代碼.然,它也提供了一個bean定義,用一個工廠方法來指向方法publicInstance().@Bean註解標記着工廠方法或其餘bean定義屬性.例如經過@Qualifier提供的一個qualifier值.其餘方法級別的註解能夠被@Scope,@Lazy或其餘自定義註解等來指定.
自動裝配的字段和方法支持上文已經討論了,另外討論的是對@Bean方法自動裝配的支持:
@Component public class FactoryMethodComponent { private static int i; @Bean @Qualifier("public") public TestBean publicInstance() { return new TestBean("publicInstance"); } // use of a custom qualifier and autowiring of method parameters @Bean protected TestBean protectedInstance( @Qualifier("public") TestBean spouse, @Value("#{privateInstance.age}") String country) { TestBean tb = new TestBean("protectedInstance", 1); tb.setSpouse(spouse); tb.setCountry(country); return tb; } @Bean private TestBean privateInstance() { return new TestBean("privateInstance", i++); } @Bean @RequestScope public TestBean requestScopedInstance() { return new TestBean("requestScopedInstance", 3); } }
上面的例子會自動裝配字符串類型的方法參數country的值爲一個名爲privateInstance的bean的Age屬性.一個spring表達式語言元素經過#{<expression>}的形式定義了表達式的值.對於@Value註解,一個表達式解析器會會再解析表達文本以前前查看bean的名稱. 在spring的Component裏的@Bean方法不一樣於@Configuration類裏的@Bean方法的處理.這個不一樣在於@Component的類沒有經過CGLIB來攔截方法或屬性的調用.CGLIB代理是經過在@Configueration類中定義了指向其餘協做對象的bean的元數據@Bean方法來調用相應的方法或字段;這些方法並無經過正常的java語義而是經過容器調用的,爲的就是當經過調用@Bean方法來引用其餘beans時能夠提供正常的生命週期管理和spring的代理.相反的,在一個簡單的@Component類裏調用@Bean方法的字段或方法會有正常的java語法,而不是一個特殊的CGLIB處理或其餘限制條件.
若是你吧一個@bean的方法宣佈爲static,這能夠容許你在bean未實例化以後調用它們.當你定義了後處理器類時,這會很是有意義.由於這些類會再容器生命週期中很早啓動,這樣會避免在此時觸發容器配置的其餘部分.
調用@bean的靜態方法不會被容器攔截,即便當你在@Configuration類裏調用.這主要是技術的限制:CGLIB子類只能重寫非靜態的方法.結果,直接調用其餘的@Bean方法擁有java標準語法,因此工廠方法自身會返回一個獨立的實例.
java語言中@Bean方法的存在並不會對spring容器裏的bean定義形成直接的影響.你能夠自由的在你認爲合適的非@Configuration類裏申明工廠方法,也能夠將之設置爲靜態方法.可是,在@Configuration類裏的常規@Bean方法可能須要重寫,因此 他們不能命名爲private或final.
@Bean方法能夠在一個指定的組件類或配置類裏發現,和java 8 發現組件類和配置類中實現接口申明的默認方法同樣.這就容許在組裝複雜的配置組合時有很大的靈活性.即便經過java8的默認方法使混合繼承成爲可能.
最後,記住一個單個的class可能對於同一個bean持有多個混合的@Bean方法,混合工廠方法的安排取決於運行時那些依賴是可得到.這個算法同其餘配置場景裏選擇最貪婪的工廠方法和構造器同樣:可適依賴數量最多的種類在構造期間獲取,同容器如何選擇混合@Autowired構造器同樣.
當一個組件做爲掃描過程的一部分進行自動檢測時,它的bean的名稱是經過BeanNameGenerator策略來告知掃描器的.默認的,spring任何固定類型註解(@Componnet,@Repository,@Service,@Controller)包含一個那麼值,並將它提供給對於的bean定義.
若是一個註解沒有包含value值或其餘可檢測的組件(能夠被自定義攔截器攔截的bean).默認的bean名稱生成器返回一個小寫字母開頭的非限制的類名稱.例如,若是如下兩個組件被檢測,那麼它們的名稱應該是myMovieListener或movieFinderImpl;
@Service("myMovieLister") public class SimpleMovieLister { // ... } @Repository public class MovieFinderImpl implements MovieFinder { // ... }
若是你不想依賴默認的bean命名策略,你能夠提供一個自定義命名策略.首先,實現BeanNameGenerator接口,並確保它有一個無參構造器.另外,在配置掃描器時提供一個全匹配符的類名稱.
@Configuration @ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class) public class AppConfig { ... }
或者
<beans> <context:component-scan base-package="org.example" name-generator="org.example.MyNameGenerator" /> </beans>
首先,思考指定註解的值,由於其餘組件可能會引用它.另外一方面,當容器進行注入時,名稱自動生成策略老是差強人意的.
通常而言,spring管理的組件默認和通用的自動檢測組件的做用域通常是單例.可是,有時你須要使用@Scope來定義其餘的做用域.使用註解提供做用域的名稱來實現.
@Scope("prototype") @Repostitory public class MovieFinderImpl implements MovieFinder{ }
web特定的做用域細節,可查看7.4.5章,Request,session,global session,application,and WebSocket scopes
若是你想使用自定義做用域策略而不是使用基於註解方法,你須要實現ScopeMetadataResolver接口,並保證該實現類有一個無參構造器.另外,在配置掃描器時提供一個類的全路徑名稱.
@Configuration @ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class) public class AppConfig { ... }
xml配置方法
<beans> <context:component-scan base-package="org.example" scope-resolver="org.example.MyScopeResolver" /> </beans>
當你使用特定非單例做用域,它可能須要爲做用域裏的對象提供代理. 這個緣由在"scoped beans as dependencies"一節裏已經描述.爲實現這個目標,component-scan元素提供了一個scoped-proxy元素,它有三個值:no,interfacce,targerClass.例如,下面的配置將會是標準的JDK動態代理:
@Configuration @ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES) public class AppConfig { ... } <beans> <context:component-scan base-package="org.example" scoped-proxy="interfaces" /> </beans>
咱們在7.9.4這節裏考慮過,'Fine-tuning annotation-based autowiring with qualifier'. 本節的例子說明在你要解決自動注入條件問題時,你可使用@Qualifier註解或自定義註解來得到更好的控制.由於這些例子都是基於xml的bean定義,這個通配符元數據經過是經過候選的bean定義使用xml裏bean元素的qualifier或meta子元素來定義.當你使用類路徑掃描或組件自動檢測,你須要提供在候選類裏經過類級別的註解來提供匹配符元數據.如下三個例子說明了這個技術:
@Component @Qualifier("Action") public class ActionMovieCatalog implements MovieCatalog { // ... } @Component @Genre("Action") public class ActionMovieCatalog implements MovieCatalog { // ... } @Component @Offline public class CachingMovieCatalog implements MovieCatalog { // ... }
做爲大部分基於xml的可替換項,要記住註解元數據是基於類定義自身的,而使用xml能夠爲同一類型的不一樣bean定義提供它們相應的配置元數據.由於註解是類級別的,而xml裏的bean則是實例級別的.