一、註解定義
@ComponentScan註解的定義如下:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
/**
* 掃描路徑
* @ComponentScan(value = "spring.annotation.componentscan")
*/
@AliasFor("basePackages")
String[] value() default {};
/**
* 掃描路徑
*/
@AliasFor("value")
String[] basePackages() default {};
/**
* 指定掃描類
* @ComponentScan(basePackageClasses = {BookDao.class, BookService.class})
*/
Class<?>[] basePackageClasses() default {};
/**
* 命名註冊的Bean,可以自定義實現命名Bean,
* 1、@ComponentScan(value = "spring.annotation.componentscan",nameGenerator = MyBeanNameGenerator.class)
* MyBeanNameGenerator.class 需要實現 BeanNameGenerator 接口,所有實現BeanNameGenerator 接口的實現類都會被調用
* 2、使用 AnnotationConfigApplicationContext 的 setBeanNameGenerator方法注入一個BeanNameGenerator
* BeanNameGenerator beanNameGenerator = (definition,registry)-> String.valueOf(new Random().nextInt(1000));
* AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
* annotationConfigApplicationContext.setBeanNameGenerator(beanNameGenerator);
* annotationConfigApplicationContext.register(MainConfig2.class);
* annotationConfigApplicationContext.refresh();
* 第一種方式只會重命名@ComponentScan掃描到的註解類
* 第二種只有是初始化的註解類就會被重命名
* 列如第一種方式不會重命名 @Configuration 註解的bean名稱,而第二種就會重命名 @Configuration 註解的Bean名稱
*/
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
/**
* 用於解析@Scope註解,可通過 AnnotationConfigApplicationContext 的 setScopeMetadataResolver 方法重新設定處理類
* ScopeMetadataResolver scopeMetadataResolver = definition -> new ScopeMetadata(); 這裏只是new了一個對象作爲演示,沒有做實際的邏輯操作
* AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
* annotationConfigApplicationContext.setScopeMetadataResolver(scopeMetadataResolver);
* annotationConfigApplicationContext.register(MainConfig2.class);
* annotationConfigApplicationContext.refresh();
* 也可以通過@ComponentScan 的 scopeResolver 屬性設置
*@ComponentScan(value = "spring.annotation.componentscan",scopeResolver = MyAnnotationScopeMetadataResolver.class)
*/
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
/**
* 用來設置類的代理模式
*/
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
/**
* 掃描路徑 如 resourcePattern = "**/*.class"
* 使用 includeFilters 和 excludeFilters 會更靈活
*/
String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
/**
* 指示是否應啓用對帶有{@code @Component},{@ code @Repository},
* {@ code @Service}或{@code @Controller}註釋的類的自動檢測。
*/
boolean useDefaultFilters() default true;
/**
* 對被掃描的包或類進行過濾,若符合條件,不論組件上是否有註解,Bean對象都將被創建
* @ComponentScan(value = "spring.annotation.componentscan",includeFilters = {
* @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class}),
* @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {SchoolDao.class}),
* @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class}),
* @ComponentScan.Filter(type = FilterType.ASPECTJ, pattern = "spring.annotation..*"),
* @ComponentScan.Filter(type = FilterType.REGEX, pattern = "^[A-Za-z.]+Dao$")
* },useDefaultFilters = false)
* useDefaultFilters 必須設爲 false
*/
Filter[] includeFilters() default {};
/**
* 指定哪些類型不適合進行組件掃描。
* 用法同 includeFilters 一樣
*/
Filter[] excludeFilters() default {};
/**
* 指定是否應註冊掃描的Bean以進行延遲初始化。
* @ComponentScan(value = "spring.annotation.componentscan",lazyInit = true)
*/
boolean lazyInit() default false;
/**
* 用於 includeFilters 或 excludeFilters 的類型篩選器
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface Filter {
/**
* 要使用的過濾器類型,默認爲 ANNOTATION 註解類型
* @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})
*/
FilterType type() default FilterType.ANNOTATION;
/**
* 過濾器的參數,參數必須爲class數組,單個參數可以不加大括號
* 只能用於 ANNOTATION 、ASSIGNABLE_TYPE 、CUSTOM 這三個類型
* @ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class, Service.class})
* @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {SchoolDao.class})
* @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
*/
@AliasFor("classes")
Class<?>[] value() default {};
/**
* 作用同上面的 value 相同
* ANNOTATION 參數爲註解類,如 Controller.class, Service.class, Repository.class
* ASSIGNABLE_TYPE 參數爲類,如 SchoolDao.class
* CUSTOM 參數爲實現 TypeFilter 接口的類 ,如 MyTypeFilter.class
* MyTypeFilter 同時還能實現 EnvironmentAware,BeanFactoryAware,BeanClassLoaderAware,ResourceLoaderAware
* 這四個接口
* EnvironmentAware
* 此方法用來接收 Environment 數據 ,主要爲程序的運行環境,Environment 接口繼承自 PropertyResolver 接口,
* 詳細內容在下方
* @Override
* public void setEnvironment(Environment environment) {
* String property = environment.getProperty("os.name");
* }
*
* BeanFactoryAware
* BeanFactory Bean容器的根接口,用於操作容器,如獲取bean的別名、類型、實例、是否單例的數據
* @Override
* public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
* Object bean = beanFactory.getBean("BeanName")
* }
*
* BeanClassLoaderAware
* ClassLoader 是類加載器,在此方法裏只能獲取資源和設置加載器狀態
* @Override
* public void setBeanClassLoader(ClassLoader classLoader) {
* ClassLoader parent = classLoader.getParent();
* }
*
* ResourceLoaderAware
* ResourceLoader 用於獲取類加載器和根據路徑獲取資源
* public void setResourceLoader(ResourceLoader resourceLoader) {
* ClassLoader classLoader = resourceLoader.getClassLoader();
* }
*/
@AliasFor("value")
Class<?>[] classes() default {};
/**
* 這個參數是 classes 或 value 的替代參數,主要用於 ASPECTJ 類型和 REGEX 類型
* ASPECTJ 爲 ASPECTJ 表達式
* @ComponentScan.Filter(type = FilterType.ASPECTJ, pattern = "spring.annotation..*")
* REGEX 參數爲 正則表達式
* @ComponentScan.Filter(type = FilterType.REGEX, pattern = "^[A-Za-z.]+Dao$")
*/
String[] pattern() default {};
}
}
節選自:@ComponentScan註解
二、使用
1.環境準備
創建Maven項目,添加依賴:
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.26.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
創建bean,controller,dao,service層,並在類上加上對應的註解,項目結構如下:
編寫測試類,如下:
public class IoCTest {
@Test
public void test01() {
//獲取Spring的IOC容器
ApplicationContext applicationContext=new AnnotationConfigApplicationContext(SpringConfig.class);
//從容器中獲取bean
String[] names= applicationContext.getBeanDefinitionNames();
for(String i:names) {
System.out.println(i);
}
}
}
2.excludeFilters的使用
使用excludeFilters不掃描com.learn包中的Controller、Service註解,如下:
@Configuration
@ComponentScan(basePackages = "com.learn",excludeFilters = {
@Filter(type = FilterType.ANNOTATION,classes = {Controller.class,Service.class})
})
public class SpringConfig {
}
上面使用的excludeFilters用於設置排除的過濾條件,實現Filter接口的type屬性用於設置過濾類型,默認值爲FilterType.ANNOTATION
,提供了這幾個過濾類型:
- FilterType.ANNOTATION:按照註解過濾
- FilterType.ASSIGNABLE_TYPE:按照給定的類型過濾
- FilterType.ASPECTJ:按照ASPECTJ表達式過濾
- FilterType.REGEX:按照正則表達式過濾
- FilterType.CUSTOM:按照自定義規則過濾
classes和value屬性爲過濾器的參數,必須爲class數組,類只能爲以下三種類型:
- ANNOTATION 參數爲註解類,如 Controller.class, Service.class,
Repository.class - ASSIGNABLE_TYPE 參數爲類,如 SchoolDao.class
- CUSTOM 參數爲實現 TypeFilter 接口的類 ,如 MyTypeFilter.class
3.includeFilters的使用
includeFilters屬性用於定義掃描過濾條件,滿足該條件才進行掃描。用法與excludeFilters一樣。
但是因爲useDefaultFilters屬性默認爲true,即使用默認的過濾器,啓用對帶有@Component,@Repository,@Service,@Controller註釋的類的自動檢測。會將帶有這些註解的類註冊爲bean裝配到IoC容器中。所以使用includeFilters時,需要把useDefaultFilters設置爲false,如下:
@Configuration
@ComponentScan(basePackages = "com.learn",includeFilters = {
@Filter(type = FilterType.ANNOTATION,classes = {Controller.class,Service.class})
},useDefaultFilters = false)
public class SpringConfig {
}
結果如下,只掃描了帶有Controller,Service註解的自定義的類:
4.自定義過濾規則
@ComponentScan註解掃描或解析的bean只能是Spring內部所定義的,比如@Component、@Service、@Controller或@Repository。如果要掃描一些自定義的註解,就可以自定義過濾規則來完成這個操作。
自定義一個類MyTypeFilter實現TypeFilter接口,這樣這個TypeFilter就掃描所有類並只通過類名包含了controller的類,如下:
public class MyTypeFilter implements TypeFilter {
/**
* 兩個參數的含義:
* metadataReader:包含讀取到的當前正在掃描的類的信息
* metadataReaderFactory:可以獲取到當前正在掃描的類的其他類信息(如父類和接口)
* match方法返回false即不通過過濾規則,true通過過濾規則
*/
@Override
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();
if(className.contains("controller")){
return true;
}
return false;
}
}
在@ComponentScan註解中進行配置,如下:
@Configuration
@ComponentScan(basePackages = "com.learn",includeFilters = {
@Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {Person.class}),
@Filter(type = FilterType.CUSTOM,classes = {MyTypeFilter.class}),
},useDefaultFilters = false)
public class SpringConfig {
}
經過上面的配置,第一個@Filter通過FilterType.ASSIGNABLE_TYPE
規定了只掃描Person類型的類,第二個@Filter通過FilterType.CUSTOM
自定義過濾規則規定了只掃描類名包含controller的類。結果: