如何从@ComponentScan包获取接口列表 [英] How to get list of Interfaces from @ComponentScan packages

查看:165
本文介绍了如何从@ComponentScan包获取接口列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想实现类似于Spring Data的东西。

I would like to implement something similar to Spring Data.

开发人员可以定义一些接口,在接口上添加自定义注释来标记它们,(我的代码将会为接口创建代理实例,并通过@Autowire将它们用于必要的服务。

Developer can define some interfaces, add a custom annotation to the interfaces to mark them, (my code will create Proxy instances for the interfaces) and use them by @Autowire to necessary services.

在spring初始化期间,我需要获取所有接口的列表(正确注释)<为接口创建动态代理并将它们注入到必要的位置。

During spring initializing I need to get list of all the interfaces (properly annotated)< create dynamic Proxy for the interfaces and inject them where they are necessary.

代理创建,创建的bean注入很好。现在问题是:

Proxy creation, created beans injecting is fine. Now the problem:

如何找到所有接口的列表?

它们可以放在任何包装中(或者甚至放在一个单独的罐子里)并且有任何名称。扫描类路径上现有的所有类需要太多时间。

They could be placed in any package (or even in a separate jar) and have any name. Scanning all the classes existing on the classpath requires too much time.

我发现问题但它需要基础包开始。

I found the question but it requires base package to start.

尝试过一个基于思考的解决方案,但它需要基础包或从root开始需要非常大的时间来扫描所有可用的类。

Tried a Reflections based solution but again it requires base package or in case of starting from root requires really a lot of time to scan all classes available.

Reflections reflections = new Reflections("...");
Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(<annotation>);

所以我需要一个基本软件包的完整列表Spring扫描在软件包中查找我的接口(必须是快得多。)

So I need a full list of base packages Spring scans to find my Interfaces in the packages (must be much much faster).

信息在SpringContext中绝对可用。我尝试调试并查看basePackages []是如何初始化的,但是有很多私有类/方法用于初始化,我只是没有看到如何从ApplicationContext正确访问basePackages。

The info is definitely available in SpringContext. I tried to debug and see how basePackages[] is initialized but there are a lot of private classes/methods used to initialize and I just don't see how to access the basePackages properly from ApplicationContext.

推荐答案

解决方案1:春天的方式

最简单的答案是遵循如何spring子项目(boot,data ...)实现了这种类型的需求。它们通常定义一个自定义组合注释,该注释启用该功能并定义一组要扫描的包。

The simplest answer is to follow how spring sub projects (boot,data...) implements this type of requirement. They usually define a custom composed annotation which enable the feature and define a set of packages to scan.

例如,给定此注释:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import({MyInterfaceScanRegistrar.class})
public @interface MyInterfaceScan {

  String[] value() default {};
}

其中定义要扫描的包和 @Import 启用 MyInterfaceScan 检测。

Where value defines the packages to scan and @Import enables the MyInterfaceScan detection.

然后创建 ImportBeanDefinitionRegistrar 。这个类将能够创建bean定义

Then create the ImportBeanDefinitionRegistrar. This class will be able to create bean definition


接口由在处理@Configuration时注册其他bean
定义的类型实现类。当
在bean定义级别(而不是@Bean
方法/实例级别)操作时是有用的或必要的。

Interface to be implemented by types that register additional bean definitions when processing @Configuration classes. Useful when operating at the bean definition level (as opposed to @Bean method/instance level) is desired or necessary.



public class MyInterfaceScanRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {
  private Environment environment;

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

  @Override
  public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    // Get the MyInterfaceScan annotation attributes
    Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(MyInterfaceScan.class.getCanonicalName());

    if (annotationAttributes != null) {
      String[] basePackages = (String[]) annotationAttributes.get("value");

      if (basePackages.length == 0){
        // If value attribute is not set, fallback to the package of the annotated class
        basePackages = new String[]{((StandardAnnotationMetadata) metadata).getIntrospectedClass().getPackage().getName()};
      }

      // using these packages, scan for interface annotated with MyCustomBean
      ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false, environment){
        // Override isCandidateComponent to only scan for interface
        @Override
        protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
          AnnotationMetadata metadata = beanDefinition.getMetadata();
          return metadata.isIndependent() && metadata.isInterface();
        }
      };
      provider.addIncludeFilter(new AnnotationTypeFilter(MyCustomBean.class));

      // Scan all packages
      for (String basePackage : basePackages) {
        for (BeanDefinition beanDefinition : provider.findCandidateComponents(basePackage)) {
          // Do the stuff about the bean definition
          // For example, redefine it as a bean factory with custom atribute... 
          // then register it
          registry.registerBeanDefinition(generateAName() , beanDefinition);
          System.out.println(beanDefinition);
        }
      }
    }
  }
}

这是逻辑的核心。 bean定义可以被操作和重新定义为具有属性的bean工厂,或者使用来自接口的生成类重新定义。

This is the core of the logic. The bean definition can be manipulated and redefined as a bean factory with attributes or redefined using a generated class from an interface.

MyCustomBean 是一个简单的注释:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCustomBean {

}

可以注释一个界面:

@MyCustomBean
public interface Class1 {

}

解决方案2:提取组件扫描

提取包的代码在 @ComponentScan 会更复杂。

The code which would extract packages define in @ComponentScan will be more complicated.

你应该创建一个 BeanDefinitionRegistryPostProcessor 并模仿 ConfigurationClassPostProcessor

You should create a BeanDefinitionRegistryPostProcessor and mimic the ConfigurationClassPostProcessor:


  • 迭代结束bean定义的bean注册表,其声明的类具有 ComponentScan 属性,例如(从 ConfigurationClassPostProcessor 中提取):

  • Iterate over the bean registry for bean definitions with a declared class having the ComponentScan attribute eg (extracted from ConfigurationClassPostProcessor.):

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
  List<BeanDefinitionHolder> configCandidates = new ArrayList<BeanDefinitionHolder>();
  String[] candidateNames = registry.getBeanDefinitionNames();
  for (String beanName : candidateNames) {
    if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
      // Extract component scan
    }
  }
}


  • 像Spring一样提取这些属性

  • Extract these attributes as Spring do

    Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
    


  • 然后扫描包并注册bean定义,就像第一个解决方案一样

  • Then scan the packages and register the bean definition like the first solution

    这篇关于如何从@ComponentScan包获取接口列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆