【问题标题】:SpringBoot not recognizing multiple @ComponentScan meta-annotationsSpringBoot 无法识别多个 @ComponentScan 元注释
【发布时间】: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 示例项目,尽管这将比上面给出的示例更详细。

标签: java spring-boot annotations component-scan


【解决方案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);
            }
        }
    }
}

【讨论】:

    猜你喜欢
    • 2021-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-24
    • 2021-01-26
    • 1970-01-01
    相关资源
    最近更新 更多