Definition of annotations

@ComponentScan Annotations are defined as follows:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {

   /**
    * 扫描路径
    * @ComponentScan(value = "spring.annotation.componentscan")
    */
   @AliasFor("basePackages")
   String[] value() default {};

   /**
    * 扫描路径
    */
   @AliasFor("value")
   String[] basePackages() default {};

   /**
    * 指定扫描类
    * @ComponentScan(basePackageClasses = {BookDao.class, BookService.class})
    */
   Class<?>[] basePackageClasses() default {};

   /**
    * 命名注册的Bean,可以自定义实现命名Bean,
    * 1、@ComponentScan(value = "spring.annotation.componentscan",nameGenerator = MyBeanNameGenerator.class)
    * MyBeanNameGenerator.class 需要实现 BeanNameGenerator 接口,所有实现BeanNameGenerator 接口的实现类都会被调用
    * 2、使用 AnnotationConfigApplicationContext 的 setBeanNameGenerator方法注入一个BeanNameGenerator
    * BeanNameGenerator beanNameGenerator = (definition,registry)-> String.valueOf(new Random().nextInt(1000));
    * AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
    * annotationConfigApplicationContext.setBeanNameGenerator(beanNameGenerator);
    * annotationConfigApplicationContext.register(MainConfig2.class);
    * annotationConfigApplicationContext.refresh();
    * 第一种方式只会重命名@ComponentScan扫描到的注解类
    * 第二种只有是初始化的注解类就会被重命名
    * 列如第一种方式不会重命名 @Configuration 注解的bean名称,而第二种就会重命名 @Configuration 注解的Bean名称
    */
   Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

   /**
    * 用于解析@Scope注解,可通过 AnnotationConfigApplicationContext 的 setScopeMetadataResolver 方法重新设定处理类
    * ScopeMetadataResolver scopeMetadataResolver = definition -> new ScopeMetadata();  这里只是new了一个对象作为演示,没有做实际的逻辑操作
    * AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
    * annotationConfigApplicationContext.setScopeMetadataResolver(scopeMetadataResolver);

    * annotationConfigApplicationContext.register(MainConfig2.class);
    * annotationConfigApplicationContext.refresh();
    * 也可以通过@ComponentScan 的 scopeResolver 属性设置
    *@ComponentScan(value = "spring.annotation.componentscan",scopeResolver = MyAnnotationScopeMetadataResolver.class)
    */
   Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;

   /**
    * 用来设置类的代理模式
    */
   ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;

   /**
    * 扫描路径 如 resourcePattern = "**/*.class"
    * 使用  includeFilters 和 excludeFilters 会更灵活
    */
   String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;

   /**
    * 指示是否应启用对带有{@code @Component},{@ code @Repository},
    * {@ code @Service}或{@code @Controller}注释的类的自动检测。
    */
   boolean useDefaultFilters() default true;

   /**
    * 对被扫描的包或类进行过滤,若符合条件,不论组件上是否有注解,Bean对象都将被创建
    * @ComponentScan(value = "spring.annotation.componentscan",includeFilters = {
    *     @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class}),
    *     @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {SchoolDao.class}),
    *     @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class}),
    *     @ComponentScan.Filter(type = FilterType.ASPECTJ, pattern = "spring.annotation..*"),
    *     @ComponentScan.Filter(type = FilterType.REGEX, pattern = "^[A-Za-z.]+Dao$")
    * },useDefaultFilters = false)
    * useDefaultFilters 必须设为 false
    */
   Filter[] includeFilters() default {};

   /**
    * 指定哪些类型不适合进行组件扫描。
    * 用法同 includeFilters 一样
    */
   Filter[] excludeFilters() default {};

   /**
    * 指定是否应注册扫描的Bean以进行延迟初始化。
    * @ComponentScan(value = "spring.annotation.componentscan",lazyInit = true)
    */
   boolean lazyInit() default false;

   /**
    * 用于 includeFilters 或 excludeFilters 的类型筛选器
    */
   @Retention(RetentionPolicy.RUNTIME)
   @Target({})
   @interface Filter {

      /**
       * 要使用的过滤器类型,默认为 ANNOTATION 注解类型
       * @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})
       */
      FilterType type() default FilterType.ANNOTATION;

      /**
       * 过滤器的参数,参数必须为class数组,单个参数可以不加大括号
       * 只能用于 ANNOTATION 、ASSIGNABLE_TYPE 、CUSTOM 这三个类型
       * @ComponentScan.Filter(type = FilterType.ANNOTATION, value = {Controller.class, Service.class})
       * @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {SchoolDao.class})
       * @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
       */
      @AliasFor("classes")
      Class<?>[] value() default {};

      /**
       * 作用同上面的 value 相同
       * ANNOTATION 参数为注解类,如  Controller.class, Service.class, Repository.class
       * ASSIGNABLE_TYPE 参数为类,如 SchoolDao.class
       * CUSTOM  参数为实现 TypeFilter 接口的类 ,如 MyTypeFilter.class
       * MyTypeFilter 同时还能实现 EnvironmentAware,BeanFactoryAware,BeanClassLoaderAware,ResourceLoaderAware 
       * 这四个接口
       * EnvironmentAware
       * 此方法用来接收 Environment 数据 ,主要为程序的运行环境,Environment 接口继承自 PropertyResolver 接口,
       * 详细内容在下方
       * @Override
       * public void setEnvironment(Environment environment) {
       *    String property = environment.getProperty("os.name");
       * }
       * 
       * BeanFactoryAware
       * BeanFactory Bean容器的根接口,用于操作容器,如获取bean的别名、类型、实例、是否单例的数据
       * @Override
       * public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
       *     Object bean = beanFactory.getBean("BeanName")
       * }
       * 
       * BeanClassLoaderAware
       * ClassLoader 是类加载器,在此方法里只能获取资源和设置加载器状态
       * @Override
       * public void setBeanClassLoader(ClassLoader classLoader) {
       *     ClassLoader parent = classLoader.getParent();
       * }
       * 
       * ResourceLoaderAware
       * ResourceLoader 用于获取类加载器和根据路径获取资源
       * public void setResourceLoader(ResourceLoader resourceLoader) {
       *     ClassLoader classLoader = resourceLoader.getClassLoader();
       * }
       */
      @AliasFor("value")
      Class<?>[] classes() default {};

      /**
       * 这个参数是 classes 或 value 的替代参数,主要用于 ASPECTJ 类型和  REGEX 类型
       * ASPECTJ  为 ASPECTJ 表达式
       * @ComponentScan.Filter(type = FilterType.ASPECTJ, pattern = "spring.annotation..*")
       * REGEX  参数为 正则表达式
       * @ComponentScan.Filter(type = FilterType.REGEX, pattern = "^[A-Za-z.]+Dao$")
       */
      String[] pattern() default {};

   }
}
Copy the code

Second, the use of

1. Environment preparation

Create Maven project and add dependencies:

<dependencies> <! -- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> < the groupId > org. Springframework < / groupId > < artifactId > spring - the context < / artifactId > < version > 4.3.26. RELEASE < / version > </dependency> <! -- https://mvnrepository.com/artifact/junit/junit --> <dependency> <groupId>junit</groupId> <artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies>Copy the code

Create the bean, Controller, DAO, service layer and annotate the class.

Write the test class as follows:

Public class IoCTest {@test public void test01() {// Get the IOC container ApplicationContext ApplicationContext =new AnnotationConfigApplicationContext(SpringConfig.class); / / from the container to get bean String [] names = applicationContext. GetBeanDefinitionNames (); for(String i:names) { System.out.println(i); }}}Copy the code

2. The use of excludeFilters

Use excludeFilters without scanning the Controller and Service annotations in the com.learn package as follows:

@Configuration @ComponentScan(basePackages = "com.learn",excludeFilters = { @Filter(type = FilterType.ANNOTATION,classes  = {Controller.class,Service.class}) }) public class SpringConfig { }Copy the code

The excludeFilters used above are used to set the Filter criteria to exclude. The type property of the Filter interface is used to set the Filter type. The default value is FilterType.ANNOTATION, which provides these Filter types:

  • Filtertype. ANNOTATION: Filter by ANNOTATION
  • Filtertype. ASSIGNABLE_TYPE: Filters according to the specified type
  • Filtertype. ASPECTJ: Filters according to ASPECTJ expressions
  • Filtertype. REGEX: filters by regular expression
  • Filtertype. CUSTOM: Filters according to CUSTOM rules

The classes and value attributes are parameters to the filter and must be class arrays. Classes can only be of one of the following types:

  • The ANNOTATION parameter is the ANNOTATION class, such as Controller.class, service. class, and repository.class
  • The ASSIGNABLE_TYPE parameter is a class, such as schooldao.class
  • The CUSTOM parameter is a class that implements the TypeFilter interface, such as MyTypeFilter.class

3. The use of includeFilters

The includeFilters attribute is used to define the scan filter criteria that are met before scanning. Use the same as excludeFilters. But because the useDefaultFilters property defaults to true, the default filters enable automatic detection of classes annotated with @Component, @repository, @Service, and @Controller. Classes with these annotations are registered as beans to be assembled into the IoC container. So when using includeFilters, set useDefaultFilters to false as follows:

@Configuration @ComponentScan(basePackages = "com.learn",includeFilters = { @Filter(type = FilterType.ANNOTATION,classes  = {Controller.class,Service.class}) },useDefaultFilters = false) public class SpringConfig { }Copy the code

The result is as follows: only custom classes with Controller, Service annotations are scanned:

4. Customize filtering rules

The @ComponentScan annotation scans or parses beans that are defined internally by Spring, such as @Component, @Service, @Controller, or @Repository. If you want to scan some custom annotations, you can customize filtering rules to do this.

Define a class MyTypeFilter that implements the TypeFilter interface, so that the TypeFilter scans all classes and only classes containing Controller by class name, as follows:

Public class MyTypeFilter implements TypeFilter {/** * * metadataReaderFactory: Can get additional class information about the currently being scanned class (such as parent classes and interfaces) * Match method returns false that does not pass the filtering rule. */ @override public Boolean match(MetadataReader MetadataReader, MetadataReaderFactory MetadataReaderFactory) throws IOException {// TODO auto-generated method stub // Obtains information about the current class annotation AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); / / get the current classes is scanning the information ClassMetadata ClassMetadata = metadataReader. GetClassMetadata (); / / get the current Resource (path) Resource Resource. = metadataReader getResource (); String className = classMetadata.getClassName(); if(className.contains("controller")){ return true; } return false; }}Copy the code

Configure it in the @ComponentScan annotation as follows:

@Configuration
@ComponentScan(basePackages = "com.learn",includeFilters = {
		@Filter(type = FilterType.ASSIGNABLE_TYPE,classes = {Person.class}),
		@Filter(type = FilterType.CUSTOM,classes = {MyTypeFilter.class}),
},useDefaultFilters = false)
public class SpringConfig {
}
Copy the code

After the above configuration, the first @filter uses filtertype. ASSIGNABLE_TYPE to scan only classes of type Person, The second @filter uses the filterType. CUSTOM CUSTOM filtering rule to scan only classes whose class name contains Controller. Results: