Sta*_*avL 7 java spring spring-boot component-scan
我想实现类似于Spring Data的东西.
开发人员可以定义一些接口,向接口添加自定义注释以标记它们(我的代码将为接口创建代理实例)并通过@Autowire将它们用于必要的服务.
在spring初始化期间,我需要获取所有接口的列表(正确注释)<为接口创建动态代理并将它们注入到必要的位置.
代理创建,创建bean注入很好.现在的问题是:
如何查找所有接口的列表?
它们可以放在任何包装中(或者甚至放在一个单独的罐子里)并且有任何名称.扫描类路径上存在的所有类需要太多时间.
我找到了问题,但它需要基础包启动.
试过一个基于思考的解决方案,但它再次需要基础包或者从root开始需要非常大的时间来扫描所有可用的类.
Reflections reflections = new Reflections("...");
Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(<annotation>);
Run Code Online (Sandbox Code Playgroud)
所以我需要一个完整的基础包列表Spring扫描在包中找到我的接口(必须要快得多).
信息在SpringContext中绝对可用.我试图调试并看看basePackages []是如何初始化的,但是有很多私有类/方法用于初始化,我只是没有看到如何从ApplicationContext正确访问basePackages.
方案一:Spring方式
最简单的答案是遵循 spring 子项目(引导、数据...)如何实现这种类型的需求。他们通常定义一个自定义组合注释,用于启用该功能并定义一组要扫描的包。
例如给出这个注释:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import({MyInterfaceScanRegistrar.class})
public @interface MyInterfaceScan {
String[] value() default {};
}
Run Code Online (Sandbox Code Playgroud)
Wherevalue定义要扫描的包并@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);
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是逻辑的核心。可以将 bean 定义操作和重新定义为具有属性的 bean 工厂,或者使用从接口生成的类重新定义。
MyCustomBean 是一个简单的注释:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyCustomBean {
}
Run Code Online (Sandbox Code Playgroud)
哪个可以注释接口:
@MyCustomBean
public interface Class1 {
}
Run Code Online (Sandbox Code Playgroud)
解决方案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
}
}
}
Run Code Online (Sandbox Code Playgroud)像 Spring 一样提取这些属性
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
Run Code Online (Sandbox Code Playgroud)然后像第一种方案一样扫描包并注册bean定义
| 归档时间: |
|
| 查看次数: |
2271 次 |
| 最近记录: |