開發中會常常使用包掃描,只要標註了@Controller、@Service、@Repository,@Component 註解的類會自動加入到容器中,ComponentScan有註解和xml配置兩種方式。java
ComponentScan.Filter[] includeFilters() default {}; 按照某些規則排除組件
ComponentScan.Filter[] excludeFilters() default {}; 指定掃描的時候只須要包含哪些組件
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
String resourcePattern() default "**/*.class";
boolean useDefaultFilters() default true;
ComponentScan.Filter[] includeFilters() default {};
ComponentScan.Filter[] excludeFilters() default {};
boolean lazyInit() default false;
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Filter {
FilterType type() default FilterType.ANNOTATION;
@AliasFor("classes")
Class<?>[] value() default {};
@AliasFor("value")
Class<?>[] classes() default {};
String[] pattern() default {};
}
}
FilterType 指定不一樣的包含/排除規則:spring
package org.springframework.context.annotation;
public enum FilterType {
ANNOTATION,
ASSIGNABLE_TYPE,
ASPECTJ,
REGEX,
CUSTOM;
private FilterType() {
}
}
一、Spring使用註解 包掃描 @ComponentScan:express
@ComponentScan("com.spring.annotation")
@Configuration
public class MainConfig {
}
注意:mainConfig 配置類也是一個組件 由於@Configuration 註解中標有@Component。數組
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
String value() default "";
}
@ComponentScan(value = "com.spring.annotation", excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class, Repository.class})
})
@Configuration
public class MainConfig {
}
三、包含過濾includeFiltersapp
若是想要只包含 Controller 註解的bean,以下配置:ide
@ComponentScan(value = "com.spring.annotation", includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})},useDefaultFilters = false)
@Configuration
public class MainConfig {
}
注意:須要添加 useDefaultFilters = false。post
四、使用@ComponentScans 來指定掃描策略this
ComponentScans 註解結構以下:spa
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
public @interface ComponentScans {
ComponentScan[] value();
}
能夠看到其內部是一個ComponentScan[] 數組,因此咱們能夠在其中指定多個ComponentScan。
例如:指定不一樣的類型,包含Controller 註解的bean 和 BookService類型的bean:
@ComponentScans(value = {
@ComponentScan(value = "com.spring.annotation", includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class}), @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookService.class})
},useDefaultFilters = false)
})
@Configuration
public class MainConfig {
}
五、FilterType.CUSTOM:使用自定義規則。
1>. 編寫MyTypeFilter 並實現 TypeFilter 接口;
2>. match方法中 實現自定義規則。
/**
* 自定義過濾規則
*/
public class MyTypeFilter implements TypeFilter {
/**
*
* @param metadataReader
* @param metadataReaderFactory
* @return
* @throws IOException
* metadataReader:讀取到的當前正在掃描的類的信息
* metadataReaderFactory:能夠獲取到其餘任何類信息的
*
*
*/
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// TODO Auto-generated method stub
//獲取當前類註解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
//獲取當前正在掃描的類的類信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
//獲取當前類資源(類的路徑)
Resource resource = metadataReader.getResource();
String className = classMetadata.getClassName();
System.out.println("--->"+className);
if(className.contains("er")){
return true;
}
return false;
}
}
3>. 使用實例(當前掃描到的類,類名中包含er,就會注入到容器中):
@ComponentScans(value = {
@ComponentScan(value = "com.spring.annotation", includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class}),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookService.class}),
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
},useDefaultFilters = false)
})
@Configuration
public class MainConfig {
}
註解小結:
@ComponentScan value:指定要掃描的包
excludeFilters = Filter[] :指定掃描的時候按照什麼規則排除那些組件
includeFilters = Filter[] :指定掃描的時候只須要包含哪些組件
FilterType.ANNOTATION:按照註解
FilterType.ASSIGNABLE_TYPE:按照給定的類型;
FilterType.ASPECTJ:使用ASPECTJ表達式
FilterType.REGEX:使用正則指定
FilterType.CUSTOM:使用自定義規則
XML配置
咱們使用component-scan來進行bean的加載,例如,咱們一般會使用以下的配置:
application.xml:
<context:component-scan base-package="com.cn.kvn.service,com.cn.kvn.config,com.baidu">
<context:include-filter type="annotation" expression="com.alibaba.dubbo.config.annotation.Service" />
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
spring-servlet.xml:
<context:component-scan base-package="com.cn.kvn.controller" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
原理:
componentScan解析bean的入口爲:ComponentScanBeanDefinitionParser#parse(Element element, ParserContext parserContext)
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE);
basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage);
String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
// Actually scan for bean definitions and register them.
ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
registerComponents(parserContext.getReaderContext(), beanDefinitions, element);
return null;
}
base-package屬性告訴spring要掃描的包,use-default-filters="false"表示不要使用默認的過濾器。
configureScanner會去配置scan時的使用的filter,其中use-default-filters屬性是來控制是否要使用默認的過濾器的(默認的過濾器會去解析base-package下的含有@Component註解的類做爲bean,其中@Repository, @Service, and @Controller都是@Component的子註解,故,默認的過濾器會將它們所有解析成bean)。
原碼中的英文註釋:
ClassPathScanningCandidateComponentProvider#ClassPathScanningCandidateComponentProvider(boolean useDefaultFilters, Environment environment)
useDefaultFilters whether to register the default filters for the @Component, @Repository, @Service, and @Controller stereotype annotations
用戶自定義的include-filter和exclude-filter會在如下方法中被解析加載。
ComponentScanBeanDefinitionParser#parseTypeFilters(Element element, ClassPathBeanDefinitionScanner scanner, ParserContext parserContext)
在執行Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);的時候,使用過濾器的順序是,exclude-filter優於include-filter。也就是說,若是同時定義了exclude-filter排除了某類(某個)bean,可是include-filter又將其包含了,則該bean不會被加載到spring容器。
ClassPathScanningCandidateComponentProvider.java /**
* Determine whether the given class does not match any exclude filter
* and does match at least one include filter.
* @param metadataReader the ASM ClassReader for the class
* @return whether the class qualifies as a candidate component
*/
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, this.metadataReaderFactory)) {
return false;
}
}
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, this.metadataReaderFactory)) {
return isConditionMatch(metadataReader);
}
}
return false;
}
附:過濾規則設置
filter標籤的type和表達式說明以下:
Filter Type |
Examples Expression |
Description |
include-filter爲例 |
annotation |
org.example.SomeAnnotation |
符合SomeAnnoation的target class |
<context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/> 表示掃描base-package下的類上加了Aspect註解的類,並註冊到spring的bean容器 |
assignable |
org.example.SomeClass |
指定class或interface的全名 |
<context:include-filter type="assignable" expression="com.test.scan.StuService"/> 指定掃描StuService類做爲bean |
aspectj |
org.example..*Service+ |
AspectJ語法 |
|
regex |
org\.example\.Default.* |
Regelar Expression |
|
custom |
org.example.MyTypeFilter |
Spring3新增自訂Type,實做org.springframework.core.type.TypeFilter |
|
注意:若是經過regex將filter的type設置成了正則表達式,注意在正則裏面.表示全部字符,而\.才表示真正的.字符。例如咱們的正則表示以Dao或者Service結束的類。
咱們也可使用annotaion來限定,以下:
<context:component-scan base-package="cn.outofmemory.spring" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>
</context:component-scan>