一、@ComponentScan 自动扫描组件
1、首先创建 BookController、BookService 以及 BookDao
package org.example.controller;
import org.springframework.stereotype.Controller;
@Controller
public class BookController {
}
package org.example.service;
import org.springframework.stereotype.Service;
@Service
public class BookService {
}
package org.example.dao;
import org.springframework.stereotype.Repository;
@Repository
public class BookDao {
}
2、在配置类上添加 @ComponentScan 注解,并指明需要扫描的包
package org.example.config;
import org.example.pojo.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("org.example")
public class MainConfig {
@Bean
public Person person(){
return new Person("lis", "25");
}
}
3.测试方法
@Test
public void testComponentScan(){
ApplicationContext ac = new AnnotationConfigApplicationContext(MainConfig.class);
String[] names = ac.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
}
4、测试结果
二、@ComponentScan 注解的其他属性
@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 ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
// 是否启用对使用 @Component @Repository、@Service 或 @Controller 注释的类的自动检测。
boolean useDefaultFilters() default true;
// 指定扫描的时候按照规则注入哪些组件
Filter[] includeFilters() default {};
// 指定扫描的时候按照规则排除哪些组件
Filter[] excludeFilters() default {};
boolean lazyInit() default false;
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface Filter {
// 指定需要过滤的类型,默认为注解类型
FilterType type() default FilterType.ANNOTATION;
@AliasFor("classes")
Class<?>[] value() default {};
// 指定需要过滤的类
@AliasFor("value")
Class<?>[] classes() default {};
String[] pattern() default {};
}
}
三、@Filter 注解
3.1 excludeFilters 属性
排除带有 @Controller 注解的类,配置类如下:
package org.example.config;
import org.example.pojo.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
@Configuration
@ComponentScan(value = "org.example", excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)})
public class MainConfig {
@Bean
public Person person(){
return new Person("lis", "25");
}
}
测试方法:
@Test
public void testComponentScan(){
ApplicationContext ac = new AnnotationConfigApplicationContext(MainConfig.class);
String[] names = ac.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
}
测试结果:
带有 @Controller 注解的类未注入到容器中。
3.2 includeFilters
若想要 includeFilters 指定的过滤规则生效,需将 @ComponentScan 的属性 useDefaultFilters 置为 false
配置类:
package org.example.config;
import org.example.pojo.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
@Configuration
@ComponentScan(value = "org.example", includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)},
useDefaultFilters = false)
public class MainConfig {
@Bean
public Person person(){
return new Person("lis", "25");
}
}
测试方法同上,测试结果:
@Service注解 以及 @Repository 注解标示的类并未注入到容器中,只有 @Controller 注解标示的类添加到了容器中。
3.3 FilterType 的过滤类型
public enum FilterType {
// 过滤类型为注解
ANNOTATION,
// 按照指定的类型进行过滤
ASSIGNABLE_TYPE,
// 按照 AspectJ 表达式进行过滤
ASPECTJ,
// 按照正则表达式进行过滤
REGEX,
// 自定义过滤规则,需实现 TypeFilter 接口
CUSTOM
}
3.4 使用自定义过滤规则
实现 TypeFilter 接口
package org.example.config;
import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import java.io.IOException;
public class MyTypeFilter implements TypeFilter {
/**
* MetadataReader:可用于读取当前正在扫描的类的信息
* MetadataReaderFactory:MetadataReader实例的工厂接口,
* 用于缓存每个原始资源的MetadataReader,提供了两个方法:
* MetadataReader getMetadataReader(String className):通过类名获取对应的MetadataReader,
* MetadataReader getMetadataReader(Resource resource):通过 Resource 资源获取MetadataReader
* */
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// 获取当前类的注解信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
// 获取当前正在扫描的类的类信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// 获取当前类资源(类的路径)
Resource resource = metadataReader.getResource();
// 获取当前类的类名
String className = classMetadata.getClassName();
System.out.println("-----------------"+className);
// 自定义过滤规则
if(className.contains("Con")){
return true;
}
return false;
}
}
配置类
package org.example.config;
import org.example.pojo.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
@Configuration
@ComponentScan(value = "org.example", includeFilters = {
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})},
useDefaultFilters = false)
public class MainConfig {
@Bean
public Person person(){
return new Person("lis", "25");
}
}
测试方法同 3.1,测试结果如下:
分析:带有虚线的类名为进入到 MyTypeFilter 类的 match 方法的 bean 的类名,但最后实际输出的只有 mainConfig、bookController,证明自定义的过滤规则生效。
总结
- @ComponentScan 注解用于类或接口上,主要指定扫描路径,Spring会把指定路径下带有指定注解的类自动装配到bean容器里。会被自动装配的注解包括@Controller、@Service、@Component、@Repository等等。
- @ComponentScan 注解需要与 @Configuration 注解搭配使用。其作用等同于 xml 配置文件中的
<context:component-scan base-package="org.example" />
配置。 - @ComponentScan 如果未指定扫描的路径,则默认的扫描路径为该注解类所在的包
- 通过 @ComponentScan 注解注入到容器中的 Bean,其名称默认为该类类名(首字母小写)
- @Configuration 注解标注的类,也会被注入到容器中,该类的名称默认为类名(首字母小写)