@ComponentScan注解原理
ComponentScan源码解析
下文源码分析版本Spring 5.2.5 release
理解@Component
@Component
作为一种由Spring容器托管的通用组件,任何被@Component
标注的组件均为组件扫描的对象。
类似的组件比如@Repository
,@Service
或者使用@Component
注解作为自定义注释。
//@see org.springframework.context.annotation.ClassPathBeanDefinitionScanner
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any (or empty String otherwise)
*/
String value() default "";
}
1. 利用@Repository
自定义一个注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
//@Component
@Repository
public @interface StringRepository {
String value() default "";
}
2. 写一个测试类NameRepository
,加上该注解
@StringRepository("chineseNameRepository")
public class NameRepository {
public List<String> findAll() {
return Arrays.asList("张三", "李四", "王二麻子");
}
}
3. 测试NameRepository
类是否能被Spring容器加载
创建一个测试类ComponentBootStrap
,代码如下
@Configuration
@ComponentScan
public class ComponentBootStrap {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ComponentBootStrap.class);
// context.register(ComponentBootStrap.class);
// context.refresh();
NameRepository nameRepository = context.getBean(NameRepository.class);
System.out.println("nameRepository.findAll() = " + nameRepository.findAll());
}
}
小提示:类NameRepository
与引导类ComponentBootStrap
需放到一个包下,才能被@ComponentScan
扫描加载。
输出结果为:nameRepository.findAll() = [张三, 李四, 王二麻子]
,这说明了自定义注解类@StringRepository
也有类似于@Component
的功能。
结论:只要你注解中包含了@Component
,就能被Spring容器托管。因为@Component
是元注解,也可以联合元注解去创造组合注解,比如@RestController
就是由@Controller
与@ResponseBody
组成的。
官网beans-meta-annotations
上面例子来源于小马哥的《Spring Boot编程思想》,NameRepository
注解不是继承@ComponentScan
,但是效果却是继承,借用小马哥对这种模式注解的定义为@ComponentScan
的派生性。那下文就探索一下@ComponentScan
派生性的原理。
探索源码
只要注解里面含有@Component
,就会被Spring扫描,并注册。为什么会这么呢?是不是跟扫描方式有关系呢?
Spring扫描bean的方式有两种,一种是自定义标签<context:component-scan base-package=“com.xxx”/>这里base-package值填写NameRepository
的包名,一种是注解@ComponentScan
,如果不填写value值,就默认扫描注解类的包下所有类。
第一种自定义标签的方式这里暂时不讨论,以后会专门写一篇来解析。
我们这里重点讲解注解@ComponentScan
方式。
1. 先看@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.cl