如何从@ComponentScan包获取接口列表

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

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

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

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

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

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

我找到了问题,但它需要基础包启动。

试过一个基于思考的解决方案,但它又要求基础包或者从root开始需要真正花费大量时间来扫描所有可用的类。

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

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

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

解决方案1:spring的方式

最简单的答案是遵循spring子项目(引导,数据……)如何实现这种类型的要求。 它们通常定义一个自定义组合注释,该注释启用该function并定义一组要扫描的包。

例如,给出这个注释:

 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Import({MyInterfaceScanRegistrar.class}) public @interface MyInterfaceScan { String[] value() default {}; } 

其中value定义要扫描的包,而MyInterfaceScan启用MyInterfaceScan检测。

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

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

 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 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工厂,或者使用来自接口的生成类重新定义。

MyCustomBean是一个简单的注释:

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

哪个可以注释一个接口:

 @MyCustomBean public interface Class1 { } 

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

提取@ComponentScan定义的包的代码将更复杂。

您应该创建一个BeanDefinitionRegistryPostProcessor并模仿ConfigurationClassPostProcessor :

  • 使用具有ComponentScan属性的声明类(例如,从ConfigurationClassPostProcessor提取)迭代Bean注册表以获取bean定义:

     public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { List configCandidates = new ArrayList(); String[] candidateNames = registry.getBeanDefinitionNames(); for (String beanName : candidateNames) { if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { // Extract component scan } } } 
  • 像Spring一样提取这些属性

     Set componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); 
  • 然后扫描包并注册bean定义,就像第一个解决方案一样

我是你的情况我会在你的BeanLocation.xml中使用类似于这个的配置,并通过像我这样的子文件夹分离proyect,我发现它很有用:

文件夹 – > java / ar / edu / unq / tip / marchionnelattenero

       

如您所见,我告诉从/ ar开始自动扫描文件夹和子文件夹中的所有组件

你可以在这里检查我的公共git项目 – > git项目

检查一下,如果有一些新问题相关,或者我对你的问题不太了解,请告诉我

我们一直这样做,没有发生任何事故。

下面是将使用List的服务bean的代码。

 @Service public class SomeService { @Autowired List myInterfaceInstances; //class stuff } 

接下来我们有接口的实现。

 @Component public class SomeImpl implements MyInterface { //class stuff } 

另一个只是为了好的措施……

 @Component public class SomeOtherImpl implements MyInterface { //class stuff }