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

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

开发人员可以定义一些接口,在接口上添加自定义批注以标记它们(我的代码将为接口创建Proxy实例),然后@Autowire将其用于必要的服务。

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

创建代理,创建bean注入就可以了。现在的问题是:

它们可以放在任何包装中(甚至放在单独的jar中),并可以使用任何名称。扫描类路径中存在的所有类需要太多时间。

我发现了问题,但它需要基本软件包才能启动。

尝试了基于反射的解决方案,但再次需要基本软件包,或者从根目录启动的情况下,确实需要大量时间来扫描所有可用的类。

Reflections reflections = new Reflections("...");

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

因此,我需要完整的基本软件包列表以进行Spring扫描,以在软件包中找到我的接口(必须快得多)。

该信息肯定在SpringContext中可用。我尝试调试,看看basePackages

[]是如何初始化的,但是有很多私有类/方法用于初始化,而我只是看不到如何从ApplicationContext正确访问basePackages。

回答:

最简单的答案是遵循spring子项目(引导,数据…)如何实现这种要求。他们通常定义一个自定义的合成批注,以启用该功能并定义一组要扫描的程序包。

例如,给出以下注释:

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

@Import({MyInterfaceScanRegistrar.class})

public @interface MyInterfaceScan {

String[] value() default {};

}

在哪里value定义要扫描的软件包并@Import启用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<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工厂进行操作和重新定义,也可以使用从接口生成的类重新定义。

MyCustomBean 是一个简单的注释:

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME)

public @interface MyCustomBean {

}

可以注释接口:

@MyCustomBean

public interface Class1 {

}

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

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

  • 遍历bean注册表以使用具有ComponentScan属性的声明类(例如从中提取ConfigurationClassPostProcessor)来定义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<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(

    sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);

  • 然后扫描软件包并像第一个解决方案一样注册Bean定义

以上是 如何从@ComponentScan软件包获取接口列表 的全部内容, 来源链接: utcz.com/qa/398790.html

回到顶部