我想实现类似于Spring Data的东西。
开发人员可以定义一些接口,向接口添加自定义注释以标记它们(我的代码将为接口创建代理实例),并通过@Autowire将它们用于必要的服务。
在spring初始化期间,我需要获取所有接口的列表(正确注释)<为接口创建动态代理并将它们注入到必要的位置。
代理创建,创建bean注入很好。现在问题是:
如何查找所有界面的列表?
它们可以放在任何包装中(或者甚至放在一个单独的罐子里)并且有任何名称。扫描类路径中存在的所有类需要太多时间。
我找到the question但它需要基础包启动。
试过一个基于思考的解决方案,但它又需要基础包,或者从root开始需要很多时间来扫描所有可用的类。
Reflections reflections = new Reflections("...");
Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(<annotation>);
所以我需要一个完整的基础包列表Spring扫描在包中找到我的接口(必须要快得多)。
SpringContext中的信息绝对可用。我尝试调试并查看basePackages []是如何初始化的,但是有很多私有类/方法用于初始化,我只是没有看到如何从ApplicationContext正确访问basePackages。
答案 0 :(得分:4)
解决方案1:春天的方式
最简单的答案是遵循spring子项目(引导,数据......)如何实现此类要求。它们通常定义一个自定义组合注释,该注释启用该功能并定义一组要扫描的包。
例如给出这个注释:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import({MyInterfaceScanRegistrar.class})
public @interface MyInterfaceScan {
String[] value() default {};
}
value
定义要扫描的包,@Import
启用MyInterfaceScan
检测。
然后创建ImportBeanDefinitionRegistrar
。该类将能够创建bean定义
由注册其他bean的类型实现的接口 处理@Configuration类时的定义。有用的时候 在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 {
}
解决方案2:提取组件扫描
提取@ComponentScan
中定义的包的代码会更复杂。
您应该创建BeanDefinitionRegistryPostProcessor并模仿ConfigurationClassPostProcessor:
使用具有ComponentScan
属性的声明类(例如,从ConfigurationClassPostProcessor
中提取)遍历bean注册表以获取bean定义。
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一样提取这些属性
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
然后扫描包并注册bean定义,如第一个解决方案
答案 1 :(得分:0)
我是你的情况我会在你的BeanLocation.xml中使用类似于这个的配置,并像我这样的子文件夹分开proyect,我发现它很有用:
文件夹 - &gt; java / ar / edu / unq / tip / marchionnelattenero
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<tx:annotation-driven transaction-manager="persistence.transactionManager" proxy-target-class="true"/>
<!-- Database Configuration -->
<!-- Auto scan the components -->
<context:component-scan base-package="ar.*"/>
</beans>
如您所见,我告诉自动扫描文件夹和子文件夹中的所有组件,从/ ar开始
您可以在此处查看我的公共git项目 - &gt; git project
检查一下,如果有一些新问题相关,或者我对你的问题不太了解,请告诉我
答案 2 :(得分:0)
我们一直这样做,没有发生任何事故。
以下是将使用List的服务bean的代码。
@Service
public class SomeService {
@Autowired
List<MyInterface> myInterfaceInstances;
//class stuff
}
接下来我们有接口的实现。
@Component
public class SomeImpl implements MyInterface {
//class stuff
}
另一个只是为了好的措施...
@Component
public class SomeOtherImpl implements MyInterface {
//class stuff
}