1、基本简介
-
@ComponentScan注解是一个Spring的注解,用于扫描包上的注解将组件加载到IOC容器中。
-
它的出现主要是对标component-scan标签,标签只能使用在xml的配置文件中,而现在越来越流行使用注解的形式注册bean
-
@ComponentScan还有个父亲是@ComponentScans;用于存放多个@ComponentScan注解。
-
@ComponentScan注解只能使用在有@Configuration配置注解的地方,因为component-scan标签必须存在于xml配置文件中;而在脱离xml使用时,需要使用带有在@Configuration配置注解上。并不局限与一定是 @Configuration也可以是@SpringBootConfiguration…
-
作用:扫描被@Controller、@Service、@Repository、@Component等注解标注的类。
-
SpringBoot的主启动类中的@SpringBootApplication会自动扫描主启动类所在的包,因此以下所有测试都是与主启动所在的包平级。
@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 {};
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 {};
}
}
主要常用的是这几个属性:
-
basePackages和value属性是同一个,指扫描哪写包下的组件注解。
-
basePackageClasses属性:扫描指定的类,该类有组件注解才能被扫描到。
-
useDefaultFilters属性:使用默认的过滤机制,常常搭配下面两个属性一起使用。
-
includeFilters属性:扫描哪些注解。
-
excludeFilters属性:不扫描哪些组件。
-
lazyInit属性:由于是包扫描一次性的,那么无法单独配置哪个组件是否懒加载,因此提供该属性用于声明是否开启懒加载。
2、基础扫描
@Component、@Controller、@Service、@Repository注解都是同样的作用,只不过后面三个是@Component注解派生出的子注解;用于MVC三层架构的不同层。
2.1、扫描包
可以一次性扫描多个包,也可以是单个包。
@Configuration
@ComponentScan(basePackages = {
"com.controller",
"com.dao",
"com.service"
})
public class MyConfig {
}
2.2、扫描类
扫描类也是需要根据组件注解扫描的,如果该类上没有组件注册的注解那么该类一样扫描不到。
@Configuration
@ComponentScan(basePackageClasses = {
TestController.class,
TestService.class,
TestDao.class //没有使用组件注解进行标注
})
public class MyConfig {
}
3、@Filter注解过滤
@ComponentScan注解中的includeFilters、excludeFilters属性用于指定组件的扫描与排除;不过更多的是使用excludeFilters排除那些不需要的组件。
-
FilterType.ANNOTATION:按照注解类型排除/包含,应该是使用最多的一个。(常用)
-
FilterType.ASSIGNABLE_TYPE:按照类型,可以是接口那么所有的实现类都会被排除/包含
-
FilterType.ASPECTJ:按照切面表达式排除/包含
-
FilterType.REGEX:按照正则表达式排除/包含
-
FilterType.CUSTOM:实现TypeFilter接口实现排除/包含(常用)
3.1、根据注解类型进行排除
这里过滤@Controller注解标注的组件
@ComponentScan(
value = {"com.controller", "com.service", "com.dao"},
excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = Controller.class)}
)
@Configuration
public class MyConfig {
}
3.2、根据注解类型进行添加
这时候需要关闭使用默认过滤,将useDefaultFilter属性置为false。
@ComponentScan(
value = {"com.controller", "com.service", "com.dao"},
useDefaultFilters = false,
includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class})}
)
@Configuration
public class MyConfig {
}
3.3、自定义表达式规则
完成自定义表达式规则需要实现TypeFilter接口,该接口非常强大。通过该类的方法可以拿到被依次扫描到的类的信息、注解、路径、甚至还可以获取到该类的祖孙三代的信息…
public class MyTypeFilter implements TypeFilter {
/**
* @param metadataReader: metadataReader扫描到的当前类
* @param metadataReaderFactory: 为父类、接口获取元数据的工厂
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// 1. 获取当前类的注解信息
AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
// 2. 获取当前类的信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// 3. 获取当前类的路径
Resource resource = metadataReader.getResource();
// 4. metadata 和 classMetadata变量又可以获取该类祖孙三代的很多信息
System.out.println("------------>" + classMetadata.getClassName());
if (classMetadata.getClassName().contains("er")){
return true; //放行所有全路径类名中包含er的
}
return false; //拒绝放行
}
}
@ComponentScan(
value = {"com.controller", "com.service", "com.dao"},
useDefaultFilters = false,
includeFilters = {@ComponentScan.Filter(type = FilterType.CUSTOM, value = {MyTypeFilter.class})}
)
@Configuration
public class MyConfig {
}
4、总结
-
@ComponentScan注解还可以重复标记,因为底层使用了@Repeatable注解标注,重复标注时会自动转化,将这一个个的@ComponentScan放入@ComponentScans中!
-
@ComponentScan注解必须依托含有@Configuration注解标注的地方。
-
当使用includeFilters属性时一定要使用useDefaultFilters = false不过这种感觉也不会使用;一般都是使用excludeFilters属性排除不想扫描的。
-
自定义的过滤规则、注解过滤规则比较常见;自定义的过滤规则十分强大可以使用多重过滤,个人认为有这一个自定义规则其他的过滤规则似乎没有存在的必要性(当然不考虑性能问题)