本文内容:
- @ComponentScan注解的作用
- basePackages的方式和basePackageClasses的方式的区别
- useDefaultFilters的作用
根据前面的文章我们知道可以使用xml中bean元素的方式和@Bean注解标注方法的方式来注册bean。
不过上面的两在方式都是单个注册的,为了更方便bean的注册,Spring提供了批量的方式注册bean,方便大量bean批量注册,spring中的@ComponentScan就是用来批量的注册bean的。
1,@ComponentScan
@ComponentScan用于批量注册bean。
这个注解会让spring去扫描某些包及其子包中所有的类,然后将满足一定条件的类作为bean注册到spring容器容器中。
具体需要扫描哪些包?以及这些包中的类满足什么条件时被注册到容器中,这些都可以通过这个注解中的参数动态配置
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
//指定需要扫描的包 如:com.chen
@AliasFor("basePackages")
String[] value() default {};
//同value;value和basePackages不能同时存在设置,可二选一
@AliasFor("value")
String[] basePackages() default {};
//指定一些类,spring容器会扫描这些类所在的包及其子包中的类
Class<?>[] basePackageClasses() default {};
//自定义bean名称生成器
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
//需要扫描包中的那些资源,默认是:"**/*.class",即会扫描指定包中所有的class文件
String resourcePattern() default "**/*.class";
//对扫描的类是否启用默认过滤器,默认为true
boolean useDefaultFilters() default true;
//过滤器:用来配置被扫描出来的那些类会被作为组件注册到容器中
ComponentScan.Filter[] includeFilters() default {};
//过滤器,和includeFilters作用刚好相反,用来对扫描的类进行排除的,被排除的类不会被注册到容器中
ComponentScan.Filter[] excludeFilters() default {};
//是否延迟初始化被注册的bean
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 {};
}
}
使用了@ComponentScan注解后,Spring会扫描指定的包,且会递归下面子包,得到一批类的数组
然后这些类会经过上面的各种过滤器,最后剩下的类会被注册到容器中。
扫描指定的包:
通过value、backPackages、basePackageClasses这3个参数来决定。
各种过滤器:
通过useDefaultFilters、includeFilters、excludeFilters这3个参数来控制过滤器
默认情况下,任何参数都不设置的情况下,此时,会将@ComponentScan修饰的类所在的包作为扫描包;默认情况下useDefaultFilters为true,这个为true的时候,spring容器内部会使用默认过滤器,规则是:凡是类上有@Repository、@Service、@Controller、@Component这几个注解中的任何一个的,那么这个类就会被作为bean注册到spring容器中,所以默认情况下,只需在类上加上这几个注解中的任何一个,这些类就会自动交给spring容器来管理了。
来个案列:
@Component
public class Car {
}
@Controller
public class CarController {
}
@Service
public class CarService {
}
1.1 未设置参数
```java
@ComponentScan
public class Scan1 {
}
默认会扫描Scan1 类所在的包中的所有类,类上有@Component、@Repository、@Service、@Controller任何一个注解的都会被注册到容器中
测试输出
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Scan1.class);
for (String name : context.getBeanDefinitionNames()) {
System.out.println(context.getBean(name));
}
}
}
org.springframework.context.annotation.ConfigurationClassPostProcessor@710726a3
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@646007f4
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@481a15ff
org.springframework.context.event.EventListenerMethodProcessor@78186a70
org.springframework.context.event.DefaultEventListenerFactory@306279ee
com.chen.Scan1@545997b1
com.chen.bean.Car@4cf4d528
com.chen.controller.CarController@77846d2c
com.chen.service.CarService@548ad73b
1.2 指定需要扫描的包
@ComponentScan(basePackages = {"com.chen.controller","com.chen.service"})
public class Scan1 {
}
指定需要扫面哪些包,可以通过value或者basePackage来配置,二者选其一,都配置运行会报错
测试输出
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Scan1.class);
for (String name : context.getBeanDefinitionNames()) {
System.out.println(context.getBean(name));
}
}
}
org.springframework.context.annotation.ConfigurationClassPostProcessor@149494d8
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@710726a3
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@646007f4
org.springframework.context.event.EventListenerMethodProcessor@481a15ff
org.springframework.context.event.DefaultEventListenerFactory@78186a70
com.chen.Scan1@306279ee
com.chen.controller.CarController@545997b1
com.chen.service.CarService@4cf4d528
可以看到Car没有注册进来
1.3 basePackageClasses指定扫描范围
@ComponentScan(basePackageClasses = CarService.class)
public class Scan1 {
}
测试输出
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Scan1.class);
for (String name : context.getBeanDefinitionNames()) {
System.out.println(context.getBean(name));
}
}
}
org.springframework.context.annotation.ConfigurationClassPostProcessor@10a035a0
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@67b467e9
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@47db50c5
org.springframework.context.event.EventListenerMethodProcessor@5c072e3f
org.springframework.context.event.DefaultEventListenerFactory@4d1b0d2a
com.chen.Scan1@954b04f
com.chen.service.CarService@149494d8
可以看到只有CarService注册进来了,所以我们可以在需要扫描的包中定义一个标记的接口或者类,作用是作为basePackageClasses的值,用来指定扫描的包。
2,@ComponentScan的includeFilters
ComponentScan.Filter[] includeFilters() default {};
@ComponentScan的includeFilters是一个Filter类型的数组,多个Filter之间为或者关系,即满足任意一个就可以了
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Filter {
//过滤器的类型,是个枚举类型
FilterType type() default FilterType.ANNOTATION;
//和参数classes效果一样,二选一
@AliasFor("classes")
Class<?>[] value() default {};
@AliasFor("value")
Class<?>[] classes() default {};
String[] pattern() default {};
}
可以看到Filter 也是一个注解,其中的属性type是枚举,它的值是FilterType 中的一个,来看一下FilterType :
public enum FilterType {
//通过注解的方式来筛选候选者,即判断候选者是否有指定的注解
ANNOTATION,
//通过指定的类型来筛选候选者,即判断候选者是否是指定的类型
ASSIGNABLE_TYPE,
//ASPECTJ表达式方式,即判断候选者是否匹配ASPECTJ表达式
ASPECTJ,
//正则表达式方式,即判断候选者的完整名称是否和正则表达式匹配
REGEX,
//用户自定义过滤器来筛选候选者,对候选者的筛选交给用户自己来判断
CUSTOM;
private FilterType() {
}
}
比如可以扫描包含我们自定义注解的类才注册进来
自定义一个注解:
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnno {
}
使用自定义注解标注:
@MyAnno
public class Car {
}
定义过滤器:
@ComponentScan(useDefaultFilters = false,includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyAnno.class)
})
public class Scan1 {
}
测试输出:
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Scan1.class);
for (String name : context.getBeanDefinitionNames()) {
System.out.println(context.getBean(name));
}
}
}
org.springframework.context.annotation.ConfigurationClassPostProcessor@2641e737
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@727803de
org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@704921a5
org.springframework.context.event.EventListenerMethodProcessor@df27fae
org.springframework.context.event.DefaultEventListenerFactory@24a35978
com.chen.Scan1@16f7c8c1
com.chen.bean.Car@2f0a87b3
可以看到只有标注了自定义注解@MyAnno的Car注册进来了
总结:
@ComponentScan用于批量注册bean,spring会按照这个注解的配置,递归扫描指定包中的所有类,将满足条件的类批量注册到spring容器中
可以通过value、basePackages、basePackageClasses 这几个参数来配置包的扫描范围
可以通过useDefaultFilters、includeFilters、excludeFilters这几个参数来配置类的过滤器,被过滤器处理之后剩下的类会被注册到容器中
指定包名的方式配置扫描范围存在隐患,包名被重命名之后,会导致扫描失败,所以一般我们在需要扫描的包中可以创建一个标记的接口或者类,作为basePackageClasses的值,通过这个来控制包的扫描范围