kni*_*ttl 5 java spring spring-annotations component-scan spring-autoconfiguration
我正在尝试@Enable
为 Spring 框架编写自己的注释,应按如下方式使用:
package com.example.package.app;
@SpringBootApplication
@com.example.annotations.EnableCustom("com.example.package.custom")
public class MyApplication {}
Run Code Online (Sandbox Code Playgroud)
我使用自定义注释遵循Component scan,但这带来了几个限制:
我不能使基本包属性动态化,即我不能传递"com.example.package.base"
,但需要在配置中预定义包。
我看了看@AliasFor
,但无法让它工作。
当我省略基础包时,扫描从注释的定义包开始,而不是从被注释的类的包开始。在上面的例子中,它只会为 中的类扫描和创建 bean com.example.annotations
,而不是为com.example.package.*
.
我看了一下EntityScanPackages.Registrar.class
在@EntityScan
注释中导入的是哪个,但它是一个内部类,我的注释无法导入。
一切正常,如果我把@ComponentScan(includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, value = MyAnnotation.class))
在MyApplication
类,但停止时这个被移动到的元注释工作@EnableCustom
。如何告诉 Spring Framework 将其视为@EnableCustom
指定@ComponentScan
某些默认值的不同方式。我试着元注解注释我用@Configuration
,@Component
和其他人,但没有成功:
@Configuration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@ComponentScan(
includeFilters = @ComponentScan.Filter(
type = FilterType.ANNOTATION,
value = ApplicationService.class))
public @interface EnableApplicationServices {
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
String[] value() default {};
}
Run Code Online (Sandbox Code Playgroud)
我在哪里可以找到这方面的文档或您会推荐什么起点?我的长期目标是拥有一个可供众多项目使用的 Spring Boot 启动器。
AM(N)WE 可以在以下存储库中找到:https : //github.com/knittl/stackoverflow/tree/spring-enable-annotation
以下是包结构的概述:
// com.example.annotations.EnableCustom.java
@Configuration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
// this annotation is never honored:
@ComponentScan(
includeFilters = @ComponentScan.Filter(
type = FilterType.ANNOTATION,
value = MyAnnotation.class))
//@Import(EnableCustom.EnableCustomConfiguration.class)
public @interface EnableCustom {
// this annotation works in combination with @Import, but scans the wrong packages.
@ComponentScan(
includeFilters = @ComponentScan.Filter(
type = FilterType.ANNOTATION,
value = MyAnnotation.class))
class EnableCustomConfiguration {}
}
// file:com.example.app.Application.java
@SpringBootApplication
@EnableCustom("com.example.app.custom.services")
// @ComponentScan(
// includeFilters = @ComponentScan.Filter(
// type = FilterType.ANNOTATION,
// value = MyAnnotation.class)) // <- this would work, but I want to move it to a custom annotation
public class Application {
}
// file:com.example.app.custom.services.MyService
@MyAnnotation
public class MyService {
public MyService() {
System.out.println("Look, I'm a bean now!");
}
}
// file:com.example.annotations.services.WrongService.java
@MyAnnotation
public class WrongService {
public WrongService() {
System.out.println("I'm in the wrong package, I must not be instantiated");
}
}
Run Code Online (Sandbox Code Playgroud)
在Fabio Formosa 的答案、该答案中填补的缺失部分以及注释中的一些灵感的帮助下@EntityScan
,我终于成功地使其发挥作用。可以在https://github.com/knittl/stackoverflow/tree/spring-enable-annotation-working找到可编译的工作示例。
简而言之:在 Fabio 的答案的基础上,ClassPathScanningCandidateComponentProvider
使用包含过滤器正确配置实例,然后针对所有提供的基类运行它非常重要。@AliasFor(annotation = Import.class, \xe2\x80\xa6)
似乎不是必需的,并且可以别名为另一个属性,例如basePackages
同一注释的属性。
最低实现如下:
\n\n@Configuration\n@Target(ElementType.TYPE)\n@Retention(RetentionPolicy.RUNTIME)\n@Import(EnableCustom.EnableCustomConfiguration.class)\npublic @interface EnableCustom {\n @AliasFor(attribute = "basePackages")\n String[] value() default {};\n\n @AliasFor(attribute = "value")\n String[] basePackages() default {};\n\n class EnableCustomConfiguration implements ImportBeanDefinitionRegistrar, EnvironmentAware {\n private static final BeanNameGenerator BEAN_NAME_GENERATOR = AnnotationBeanNameGenerator.INSTANCE;\n private Environment environment;\n\n @Override\n public void setEnvironment(final Environment environment) {\n this.environment = environment;\n }\n\n @Override\n public void registerBeanDefinitions(\n final AnnotationMetadata metadata,\n final BeanDefinitionRegistry registry) {\n final AnnotationAttributes annotationAttributes = new AnnotationAttributes(\n metadata.getAnnotationAttributes(EnableCustom.class.getCanonicalName()));\n\n final ClassPathScanningCandidateComponentProvider provider\n = new ClassPathScanningCandidateComponentProvider(false, environment);\n provider.addIncludeFilter(new AnnotationTypeFilter(MyAnnotation.class, true));\n\n final Set<String> basePackages\n = getBasePackages((StandardAnnotationMetadata) metadata, annotationAttributes);\n\n for (final String basePackage : basePackages) {\n for (final BeanDefinition beanDefinition : provider.findCandidateComponents(basePackage)) {\n final String beanClassName = BEAN_NAME_GENERATOR.generateBeanName(beanDefinition, registry);\n if (!registry.containsBeanDefinition(beanClassName)) {\n registry.registerBeanDefinition(beanClassName, beanDefinition);\n }\n }\n }\n }\n\n private static Set<String> getBasePackages(\n final StandardAnnotationMetadata metadata,\n final AnnotationAttributes attributes) {\n final String[] basePackages = attributes.getStringArray("basePackages");\n final Set<String> packagesToScan = new LinkedHashSet<>(Arrays.asList(basePackages));\n\n if (packagesToScan.isEmpty()) {\n // If value attribute is not set, fallback to the package of the annotated class\n return Collections.singleton(metadata.getIntrospectedClass().getPackage().getName());\n }\n\n return packagesToScan;\n }\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n
归档时间: |
|
查看次数: |
1360 次 |
最近记录: |