使用@ComponentScan元注释的Spring自定义@Enable批注

2020年7月12日 10点热度 0条评论

我正在尝试为Spring框架编写自己的@Enable批注,该批注应如下使用:

package com.example.package.app;

@SpringBootApplication
@com.example.annotations.EnableCustom("com.example.package.custom")
public class MyApplication {}

我关注了
Component scan using custom annotation,但这带来了一些限制:

  • 我无法使基本包属性动态化,即无法传递"com.example.package.base",但需要在配置中预定义包。

    我看了@AliasFor,但是无法正常工作。

  • 当我忽略基本包时,扫描从注释的定义包开始,而不是从带注释的类的包开始。在上面的示例中,它将仅扫描并创建com.example.annotations中的类的bean,而不扫描com.example.package.*

    我看了在EntityScanPackages.Registrar.class批注中导入的@EntityScan,但这是一个内部类,我的批注无法导入。

  • 如果将
    @ComponentScan(includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, value = MyAnnotation.class))放在
    MyApplication类上,则一切正常,但是当将其移至
    @EnableCustom的元注释时,它将停止工作。如何告诉Spring Framework将
    @EnableCustom视为通过一些默认值指定
    @ComponentScan的另一种方式。我尝试使用
    @Configuration
    @Component和其他注释进行元注释,但无济于事:

    @Configuration
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @ComponentScan(
            includeFilters = @ComponentScan.Filter(
                    type = FilterType.ANNOTATION,
                    value = ApplicationService.class))
    public @interface EnableApplicationServices {
        @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
        String[] value() default {};
    }
    

    在哪里可以找到有关此文档的文档,或者您会推荐什么起点?我的长期目标是拥有一个可以被多个项目使用的Spring Boot启动器。

    在以下存储库中可以找到M(N)WE:
    https://github.com/knittl/stackoverflow/tree/spring-enable-annotation

    这是包结构的简要说明:

    // com.example.annotations.EnableCustom.java
    @Configuration
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    // this annotation is never honored:
    @ComponentScan(
            includeFilters = @ComponentScan.Filter(
                    type = FilterType.ANNOTATION,
                    value = MyAnnotation.class))
    //@Import(EnableCustom.EnableCustomConfiguration.class)
    public @interface EnableCustom {
        // this annotation works in combination with @Import, but scans the wrong packages.
        @ComponentScan(
                includeFilters = @ComponentScan.Filter(
                        type = FilterType.ANNOTATION,
                        value = MyAnnotation.class))
        class EnableCustomConfiguration {}
    }
    
    // file:com.example.app.Application.java
    @SpringBootApplication
    @EnableCustom("com.example.app.custom.services")
    // @ComponentScan(
    //         includeFilters = @ComponentScan.Filter(
    //                 type = FilterType.ANNOTATION,
    //                 value = MyAnnotation.class)) // <- this would work, but I want to move it to a custom annotation
    public class Application {
    }
    
    // file:com.example.app.custom.services.MyService
    @MyAnnotation
    public class MyService {
        public MyService() {
            System.out.println("Look, I'm a bean now!");
        }
    }
    
    // file:com.example.annotations.services.WrongService.java
    @MyAnnotation
    public class WrongService {
        public WrongService() {
            System.out.println("I'm in the wrong package, I must not be instantiated");
        }
    }
    

    解决方案如下:

    将自定义注释@EnableAnnotation与 basePackages 属性一起使用

    @EnableAnnotation(basePackages =  "write-here-a-base-package")
    @Configuration
    @EnableAutoConfiguration
    @ComponentScan
    public class SampleSimpleApplication implements CommandLineRunner {
    
      public static void main(String[] args) throws Exception {
       SpringApplication.run(SampleSimpleApplication.class, args);
      }
    }
    


    @EnableAnnotation 的定义如下:

    @Retention(RUNTIME)
    @Target(TYPE)
    @Import(EnableAnnotationConfigRegistrar.class)
    public @interface EnableAnnotation {
    
      String[] basePackages() default "*";
    
      @AliasFor(annotation = Import.class, attribute = "value")
      Class<?>[] value() default { EnableAnnotationConfigRegistrar.class };
    
    }
    

    最后,
    EnableAnnotationConfigRegistrar.class 以编程方式进行扫描:

    public class EnableAnnotationConfigRegistrar implements ImportBeanDefinitionRegistrar {
    
     @Override
     public void registerBeanDefinitions(AnnotationMetadata enableAnnotationMetadata,
      BeanDefinitionRegistry registry) {
       AnnotationAttributes enableAnnotationAttributes = new AnnotationAttributes(
       enableAnnotationMetadata.getAnnotationAttributes(EnableAnnotation.class.getName()));
    
       String[] basePackages = enableAnnotationAttributes.getStringArray("basePackages");
       AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(basePackages);
       }
    
    }