本文介紹經過掃描類路徑隱式檢測候選組件的方法。候選組件是符合過濾條件的類而且在容器中有一個相應的bean定義。這移除了經過XML表示bean註冊的須要;做爲替代,可使用註解(例如@Component)、AspectJ 類型表達式、或者自定義的過濾條件來選擇什麼類能夠在容器中註冊bean定義。正則表達式
從Spring 3.0開始,Spring JavaConfig工程提供的許多功能成爲Spring Framework核心的一部分。這容許使用Java定義bean而不是使用傳統的XML文件。查看@Configuration、@Bean、@Import和@DependsOn註解來獲取如何使用新功能的例子。算法
@Respository註解是任何知足角色或者原型爲repository(也被橫位數據接入對象或DAO)類的標記。此標記的用途之一是自動翻譯異常。spring
Spring提供更多的原型註解:@Component、@Service和@Controller。@Component是一個範型的原型,對任意Spring管理的組件都適用。@Repository、@Service和@Controller是@Component的特殊化,用於更具體的狀況,例如,各自應用於在持久層、服務層和表現層。所以,可使用@Component註釋組件類,可是使用@Respository、@Service和@Controller替代@Component註釋它們,使class更適合於工具處理或者與面相關聯。例如,這些原型註解是切入點的理想目標。同時,在Spring將來的發行版中@Repository、@Service和@Controller也許會攜帶更多的語義。所以,若是在業務層選擇使用@Component仍是@Service,@Service是更好的選擇。與上文所述類似,@Repository已經被支持爲持久層的自動翻譯異常的標記。express
Spring提供的許多註解均可以做爲元註解在代碼中使用。元註解就是應用於其餘註解的註解。例如,上文所述@Service註解中就使用了@Component做爲元註解:編程
@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組成。安全
此外,組合的註解能夠從新聲明元註解的屬性用於用戶自定義。這當你想要僅僅暴露元註解的一部分屬性時十分有用。例如,Spring的@SessionScope註解硬編碼做用域名爲session可是仍然容許自定義proxyMode。session
@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:ide
@Service @SessionScope public class SessionScopedService { // ... }
或者覆蓋proxyMode的值以下:函數
@Service @SessionScope(proxyMode = ScopedProxyMode.INTERFACES) public class SessionScopedUserService implements UserService { // ... }
Spring能夠自動檢測原型類而且使用ApplicationContext註冊相關的BeanDefinition。例如如下兩個類能夠被自動檢測:工具
@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,須要添加@ComponentScan到註釋了@Configuration的類,其中basePackages屬性是上面兩個類共同的父包。(另外一種方法是,指定逗號/分號/空格分隔的列表包含每一個類的父包名。)
@Configuration @ComponentScan(basePackages = "org.example") public class AppConfig { ... }
爲了簡潔,上面的例子可使用註解的value屬性,即@ComponentScan("org.example")
除了將包設置爲簡單的String類型以外,@ComponentScan還提供了另一種方法,即將其指定爲包中所包含的類或接口。
@Configuration @ComponentScan(basePackageClasses={CDPlayer.class, DVDPlayer.class}) public class CDPlayerConfig { }
basePackages屬性被替換成了basePackageClasses。同時再也不使用String類型的名稱指定包,使用String方法是類型不安全的。
下面的例子是使用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-config的功能。當使用context:component-scan是一般沒有必要使用context:annotation-config元素。
類路徑包的掃描須要在類路徑中存在相應的目錄條目。當使用Ant編譯JARs時,保證沒有激活JAR認爲的僅文件切換。(???)同時,在一些環境中基於安全策略,類路徑文件夾也許不會被暴露,例如JDK 1.7.0_45或更高版本的獨立應用(這須要在清單中設置「信任的庫」)。
此外,但使用組件掃描元素時AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor被隱式的包含。這意味着這兩個組件被自動檢測並裝配,全部這些都不須要在XML中提供任何bean配置元數據。
能夠經過包含值爲false的annotation-config屬性(context:component-scan元素有,但@ComponentScan註解沒有這個屬性)來禁用AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor的註冊。
默認的,註釋了@Component、@Repository、@Service、@Controller或者用戶自定義的使用了@Componnet的註解的類是被檢測的候選組件。然而,能夠經過應用用戶定義的過濾器修改和擴展這個行爲。將它們添加到@ComponentScan註解的includeFilters或excludeFilters參數中(或者做爲component-scan元素的include-filter或者excludeFilters子元素)。每一個過濾器元素須要type和expression屬性。下面的表格描述過濾選項。
過濾器類型 | 表達式示例 | 描述 |
---|---|---|
annotation(註解默認) | org.example.SomeAnnotation | 使用在目標組件的類級別上 |
assignable(分配) | org.example.SomeClass | 目標組件分配去(擴展/實現)的類(接口) |
aspectj | org.example..*Service+ | AspectJ 類型表達式來匹配目標組件 |
regex(正則表達式) | org.example.Default.* | 正則表達式來匹配目標組件類的名稱 |
custom(自定義) | org.example.MyTypeFilter | 自定義org.springframework.core.type.TypeFilter 接口的實現類 |
下面的例子展現了忽略全部@Repository註解而且使用「stub」repositories做爲替代:
@Configuration @ComponentScan(basePackages = "org.example", includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"), excludeFilters = @Filter(Repository.class)) public class AppConfig { ... }
等價的XML配置:
<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>
能夠在註解上設置useDefaultFilters=false或提供use-default-filters="false"屬性給<component-scan/>元素來關閉默認過濾器。這實際上會關閉對@Component、@Repository、@Service、@Contoller或@Configuration的自動檢測。
Spring組件也能夠向容器提供bean定義元數據。可使用@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指定一個限定值。能夠指定的其它方法級的註解有@Scope、@Lazy和自定義限定符註解。
除了初始化組件的做用,@Lazy註解也能夠放置在標有@Autowired或@Inject的注入點。在這種狀況下,它會致使注入一個延遲解析代理。
自動裝配的字段和方法如前所述,還有對@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); } }
這個例子自動裝配String類型的方法參數country爲另外一個名爲privateInstance的bean的Age屬性。一個Spring表達式語言元素經過符號#{<expression>}定義了屬性的值。對於@Value註解,預配置的表達式解析起在解析表達式文本時查找bean的名字。
在Spring Framework 4.3中,能夠定義一個類型爲InjectionPoint(或者它的更具體的子類DependencyDescriptor)工廠方法參數用於訪問觸發建立當前bean的請求注入點。須要注意的是它僅會應用於實際建立bean的實例時,而不是注入已存在的bean時。所以,該功能對於做用域爲prototype的bean是最有意義的。對於其它做用域,工廠方法只會看到在給定範圍內出發建立新bean實例的注入點:例如,依賴出發了一個懶加載singleton bean的建立。在這種狀況下,使用提供的注入點元數據進行語義關注(???)。
@Component public class FactoryMethodComponent { @Bean @Scope("prototype") public TestBean prototypeInstance(InjectionPoint injectionPoint) { return new TestBean("prototypeInstance for " + injectionPoint.getMember()); } }
常規Spring組件中的@Bean方法的處理方式與Spring @Configuration類的方法不一樣。不一樣之處子在於,不會使用CGLIB加強@Component類來攔截方法和字段的調用。CGLIB代理是調用@Configuration 類中的@Bean方法中的方法或字段來建立b引用協做對象的ean元數據的手段;這樣的方法不是用正常的Java語義來調用的,而是經過容器來提供Spring bean的生命週期和代理,即便經過對@Bean方法的編程調用來引用bean也同樣。相反,在普通的@Component類的@Bean方法中調用一個方法或字段有用普通Java語義,沒有特殊的CGLIB處理或其它約數。
能夠將@Bean方法聲明爲static,容許在不建立包含它的容器配置類實例的狀況下調用它們。這在定義後處理器bean(例如BeanFactoryPostProcessor或BeanPostProcessor)時十分有用,由於這樣的bean將在容器生命週期的早期初始化而且應避免在此時出發配置的其它部分。
請注意,靜態@Bean方法的調用永遠不會被容器攔截,甚至是@Configuration類。這是因爲技術限制:CGLIB字累僅能覆蓋非靜態方法。所以,直接調用另外一個@Bean方法會使用標準Java語義,致使直接從工廠方法返回一個獨立的實例。
@Bean方法的Java語言可見性對於Spring容器中的bean定義沒有直接的影響。能夠隨意的聲明非@Configuration類的工廠方法和任何地方的靜態方法。可是@Configuration類中的常規@Bean方法必須是可覆蓋的,即不能將其聲明爲私有或fianl。
在給定的組件或配置類的基類中的@Bean方法也將會發現,同時Java 8中組件或配置類實現的接口中的@Bean方法也會被發現。這在組合複雜的配置佈局方面具備很大的靈活性,甚至經過Spring4.2支持的Java 8默認方法能夠實現多重繼承。
最後要注意的是,一個類能夠持有同一個bean的多個@Bean方法,做爲根據運行時可用依賴關係使用的多個工廠方法的排列。這一個在其它場景中選擇最「貪婪」的構造函數和工廠方法的算法相同:在構造時選擇具備最多依賴關係的方法,相似於容器在多個@Autowired構造函數之間的選擇。
當組件被掃描程序自動發現,它的bean名字由掃描器已知的BeanNameGenerator策略生成。默認值,任意Spring遠行註解(@Component、@Repository、@Service和@Controller)包含name值從而將這個名字提供給相應的bean定義。
若是註解不包含name值(例如由自定義過濾器發現的註解),默認的bean名字生成器返回沒有限定值的首字母小寫的類名。例以下面兩個組件被發現,名字應爲myMovieLister和movieFinderImpl:
@Service("myMovieLister") public class SimpleMovieLister { // ... }
@Repository public class MovieFinderImpl implements MovieFinder { // ... }
若是不想使用默認的bean命名策略,能夠提供自定義的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>
做爲通常規則,只要其它組件可能對其進行顯示引用,請考慮使用註解指定它的名字。另外一方面,只要容器負責自動裝配,自動生成的名字就足夠了。
還有另一種爲bean命名的方式,這種方式不使用@Component註解,而是使用Java依賴注入規範中提供的@Named註解。Spring支持將@Named做爲@Component的替代方案。在「使用JSR 330 標準註解」一章介紹。
與通常的Spring管理的組件同樣,自動檢測組件的默認和最多見的做用域時singleton。然而,有時須要不一樣的做用域,可使用@Scope註解指定。在註解中提供做用域的名稱便可:
@Scope("prototype") @Repository public class MovieFinderImpl implements MovieFinder { // ... }
爲了提供自定義做用域方案能夠而不是依賴基於註解的方法,實現ScopeMetadataResolver接口,而且確保它有默認無參數的構造方法。而後在配置掃描器時提供全限定類名:
@Configuration @ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class) public class AppConfig { ... }
<beans> <context:component-scan base-package="org.example" scope-resolver="org.example.MyScopeResolver" /> </beans>
當使用某個非singleton做用域,可能須要爲做用域對象生成代理(因爲不一樣做用域之間的依賴致使的問題)。爲了這個目的,組件掃描元素有一個scoped-proxy屬性。有三個有效的值:no、interface和targetClass。例如,下面的配置將使用標準JDK動態代理:
@Configuration @ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES) public class AppConfig { ... }
<beans> <context:component-scan base-package="org.example" scoped-proxy="interfaces" /> </beans>
在XML配置中使用bean元素的quialifier或meta子元素提供候選bean定義的限定值元數據。當使用類路徑掃描來自動檢測組件,使用類型級別的註解來爲候選類提供限定值元數據。下面的三個例子展現這個技術:
@Component @Qualifier("Action") public class ActionMovieCatalog implements MovieCatalog { // ... }
@Component @Genre("Action") public class ActionMovieCatalog implements MovieCatalog { // ... }
@Component @Offline public class CachingMovieCatalog implements MovieCatalog { // ... }
與大多數基於註解的方法同樣,記住註解元數據被綁定到類定義自己,而使用XML容許實例化同一個bean定義的多個bean,這樣能夠提供不一樣的限定值元數據,由於元數據是每一個實例一份,而不是每一個類一份。