SpringBoot 无法识别多个 @ComponentScan 元注释

Posted

技术标签:

【中文标题】SpringBoot 无法识别多个 @ComponentScan 元注释【英文标题】:SpringBoot not recognizing multiple @ComponentScan meta-annotations 【发布时间】:2021-11-14 03:49:21 【问题描述】:

我注意到 Spring 的 @ComponentScan 注释在用作元注释时存在问题。

在以下示例项目结构中,FirstHandlerSecondService 类都应作为组件进行扫描并注册为 bean:

org/example/
|_ ExampleContext.java
|___ api/
| |___ ExampleCommand.java
|___ application/
  |___ FirstHandler.java
  |___ SecondService.java
// --- ExampleContext.java ---
@ContextConfiguration
public class ExampleContext  

// --- api/ExampleCommand.java ---
public class ExampleCommand extends Command 
  // -snip-


// --- application/FirstHandler.java ---

public class FirstHandler implements CommandHandler<ExampleCommand> 
  // -snip-


// --- application/SecondService.java ---

@CommandService
public class SecondService 
  @CommandMethod(ExampleCommand.class)
  public void handle(ExampleCommand command) 
    // -snip-
  

Command 和相关类是自定义的,与手头的问题无关。出于这个问题的目的,它们充当标记并驻留在不依赖于 Spring 的模块中,因此不能对自身进行元注释。 自定义注释ContextConfiguration 应该扫描所有实现CommandHandler&lt;C&gt; 或使用CommandService 注释的类:

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Configuration
@ComponentScans(
  // Scan for CommandHandler implementations
  @ComponentScan(includeFilters = 
          @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = 
                  CommandHandler.class,
          )
  ),
  // Scan for @CommandService annotated classes
  @ComponentScan(includeFilters = 
          @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = 
                  CommandService.class,
          )
  )
)
public @interface ContextConfiguration  

但实际发生的是使用了第一个@ComponentScan注解,而第二个注解被简单地忽略了。 通过更改注释的顺序或删除一个,我可以更改哪个被忽略/激活,但 Spring 只扫描其中一个类。

这是一个已知问题吗? 有什么解决方案/变通办法吗?

谢谢你,祝你有美好的一天, 亚历克斯。

【问题讨论】:

注意:如果需要完整的项目示例,我可以创建一个公共 GitHub 示例项目,尽管这将比上面给出的示例更详细。 【参考方案1】:

我找到了一种解决方法,它基本上重新实现了重复的@ComponentScan 注释应该 做的事情。 它受到this answer 的启发,并略微简化了实现。

要使用多个不同的组件扫描,可以在注释上导入实现ImportBeanDefinitionRegistrar 的嵌套配置。

例子:

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Configuration
@Import(ContextConfiguration.ScanContextComponents.class)
public @interface ContextConfiguration 

    class ScanContextComponents implements ImportBeanDefinitionRegistrar, EnvironmentAware 
        private Environment environment;

        @Override
        public void setEnvironment(Environment environment) 
            this.environment = environment;
        

        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingMetadata, BeanDefinitionRegistry registry) 
            String basePackage = ClassUtils.getPackageName(importingMetadata.getClassName());
            ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);

            // Add include-filters for different types here
            provider.addIncludeFilter(new AssignableTypeFilter(CommandHandler.class));
            provider.addIncludeFilter(new AnnotationTypeFilter(CommandService.class, true));

            // Register bean-definition candidates
            provider.setEnvironment(environment);
            for (BeanDefinition beanDefinition : provider.findCandidateComponents(basePackage)) 
                String beanName = AnnotationBeanNameGenerator.INSTANCE.generateBeanName(beanDefinition, registry);
                registry.registerBeanDefinition(beanName, beanDefinition);
            
        
    

【讨论】:

以上是关于SpringBoot 无法识别多个 @ComponentScan 元注释的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的 @Aspect 无法被我的 SpringBoot 应用程序识别?

springboot无法识别配置文件级解决办法

SpringBoot 无法从多模块 Java 应用程序中的另一个模块识别 RestController

Spring Boot + Thymeleaf Security 无法识别

Spring Boot 2.4.3 无法识别我的“.jsp”模板

Springboot 生成验证码