Spring Boot注解之@ComponentScan用法和实现原理

注解 @ComponentScan 的作用

  @Component 注解及其衍生注解 @RestController、@Controller、@Service 和 @Repository 都是组件注册注解。@ComponentScan 注解主要是从约定的扫描路径中,识别标注了组件注册注解的类,并且把这些类自动注册到 spring IoC 容器中,这些类就是我们通常所言的 bean。IoC 容器是 Spring 的特色之一,可以使用它管理 bean。当然,@Configration 注解修饰的类也会被托管给 IoC 容器。

  举个例子,你在微博上 @某某,对方会优先看到这条信息,并给你反馈。同理,在 Spring 中,你标识一个 @符号,那么 Spring 就会关照一下,从你这里拿到一个 Bean(自动注册)或者给出一个 Bean(自动装配)。

  思考:Spring 怎么知道哪些 Java 类应该当作 bean 注册到 IoC 容器中?

  解析:使用配置文件或者注解的方式进行标识需要处理的 java 类,这些被标注的类被 Spring 识别为 bean 类。

组件扫描路径

  注解 @ComponentScan 如果不设置 value 属性,默认扫描路径是启动类 XxxApplication.java 所在目录及其子目录,所以最好还是配置 value 属性,减少加载时间,提高系统启动速度。

  比如启动类在包 com.eg.wiener 下面,那么项目启动时,会默认扫描 wiener 包及其子包下的所有类。也就是说,即便不明确标注 @ComponentScan,Spring Boot 也会自动搜索当前应用主入口目录及其下方子目录。如果其它包中的 bean 不在当前主包路径下面,则应使用 @ComponentScan 设置 value 属性,配置扫描路径。如果定义了错误的扫描路径,那么在使用注解 @Autowired 自动装配 Bean 时会出错,报 a bean of type that could not be found 错误。

配置扫描路径

  @ComponentScan 注解既可以扫描包,也可以扫描指定的类。我们只需要指定一个需要扫描的路径,就可以达到更改扫描路径的目的。

  1. 包路径

  通过 value 属性设置需要扫描的包:

@ComponentScan({"com.company.user","com.company.service"})

  1. 类路径

  通过 basePackageClasses 属性指定需要扫描的类:

@ComponentScan(basePackageClasses={XxxService.class, YyyService.class})

@Component 和 @ComponentScan 的区别

  @Component 和 @ComponentScan 有什么区别?二者用于不同目的,咱们结合学生抢答老师的问题这一场景来解释。@Component 表示哪些类可能是 bean 的候选者,就像哪些同学举手抢答问题一样。@ComponentScan 搜索组件包中的类,找到所有 bean 的候选者,就像老师试图找出哪些同学举手抢答问题。通俗一点解释,就是全班同学代表扫描路径下的所有类,抢答者就是被 @Component 注解修饰的类,而找到所有抢答者的老师就是 @ComponentScan。延伸一点,成功抢到答题机会的同学就是被 @Autowire 装配的 bean 了。

注解 @ComponentScan 实现原理

  本节介绍 Spring Boot 中注解 @ComponentScan 的实现原理。

  下面介绍一下 ComponentScan 注解中几个常用的属性。

  1. String[] value() default {};
    指定包扫描路径,value 属性的值,就是项目中的一个具体路径。value 属性的类型是 String 数组,也就是支持一次指定多个包扫描路径。这个属性上面添加了一个注解,@AliasFor("basePackages"),这个注解的意思就是说,value 这个属性等价于 basePackages 属性。关于 basePackages 属性,下面会讲到。
  2. Class<?>[] basePackagesClasses() default {};
    扫描具体的类。basePackagesClasses 属性的类型是 Class 数组,也就是说支持同时指定多个扫描类。
  3. Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator. class;
    配置 beanName 生成器,默认是 BeanNameGenerator。一般情况下,我们都是使用默认的 beanName 生成器,但是 Spring 实现了 beanName 生成器的可配置。
  4. boolean useDefaultFilters() default true;
    是否对含有以下注解的类开启检测,默认是开启的。
    @Component
    @Repository
    @Service
    @Controller
  5. ComponentScan.Filter[] includeFilters() default {};
    指定某些 Filter 扫描到的类。听起来有些费劲,说白了就是指定了类型,扫描指定的这些类型。可选类型有 5 种,定义在枚举类 FilterType 中:
    第一种:ANNOTATION
    第二种:ASSIGNABLE_TYPE
    第三种:ASPECTJ
    第四种:REGEX,正则表达式。
    第五种:CUSTOM,自定义类型。
  6. ComponentScan.Filter[] excludeFilters() default {};
    排除过滤器扫描的的类。

Reference