如何从@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 }