当前位置: 首页 > 知识库问答 >
问题:

Spring自定义@enable注释元注释与@componentscan

侯善
2023-03-14
package com.example.package.app;

@SpringBootApplication
@com.example.annotations.EnableCustom("com.example.package.custom")
public class MyApplication {}

>

  • 我不能使基本包属性是动态的,即我不能传递“com.example.package.base”,但需要在配置中预先定义包。

    我查看了@aliasfor,但无法使其工作。

    当我忽略基本包时,扫描从注释的定义包开始,而不是从注释类的包开始。在上面的示例中,它只扫描并创建com.example.annotations中类的bean,而不扫描并创建com.example.package.*中的bean。

    如果将@componentscan(includeFilters=@componentscan.filter(type=filtertype.annotation,value=myAntation.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 {};
    }
    

    我在哪里可以找到这方面的文档,或者你会推荐什么起点?我的长期目标是拥有一个可以被大量项目使用的Spring Boot starter。

    我们可以在以下存储库中找到M(N):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");
        }
    }
    
  • 共有1个答案

    锺离声
    2023-03-14

    借助Fabio Formosa的答案、从这个答案中填写的缺失部分,以及@entityscan注释的一些启发,我终于成功地实现了这一点。可以在https://github.com/knittl/stackoverflow/tree/spring-enable-annotation-working找到一个可编译的工作示例。

    简而言之:基于Fabio的回答,使用include筛选器正确配置ClassPathScanningCandidateComponentProvider实例,然后针对所有提供的基类运行它是很重要的。@aliasfor(annotation=import.class,…)似乎不是必需的,可以将其别名为另一个属性,例如相同注释的basePackages

    最低执行情况如下:

    @Configuration
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Import(EnableCustom.EnableCustomConfiguration.class)
    public @interface EnableCustom {
        @AliasFor(attribute = "basePackages")
        String[] value() default {};
    
        @AliasFor(attribute = "value")
        String[] basePackages() default {};
    
        class EnableCustomConfiguration implements ImportBeanDefinitionRegistrar, EnvironmentAware {
            private static final BeanNameGenerator BEAN_NAME_GENERATOR = AnnotationBeanNameGenerator.INSTANCE;
            private Environment environment;
    
            @Override
            public void setEnvironment(final Environment environment) {
                this.environment = environment;
            }
    
            @Override
            public void registerBeanDefinitions(
                    final AnnotationMetadata metadata,
                    final BeanDefinitionRegistry registry) {
                final AnnotationAttributes annotationAttributes = new AnnotationAttributes(
                        metadata.getAnnotationAttributes(EnableCustom.class.getCanonicalName()));
    
                final ClassPathScanningCandidateComponentProvider provider
                        = new ClassPathScanningCandidateComponentProvider(false, environment);
                provider.addIncludeFilter(new AnnotationTypeFilter(MyAnnotation.class, true));
    
                final Set<String> basePackages
                        = getBasePackages((StandardAnnotationMetadata) metadata, annotationAttributes);
    
                for (final String basePackage : basePackages) {
                    for (final BeanDefinition beanDefinition : provider.findCandidateComponents(basePackage)) {
                        final String beanClassName = BEAN_NAME_GENERATOR.generateBeanName(beanDefinition, registry);
                        if (!registry.containsBeanDefinition(beanClassName)) {
                            registry.registerBeanDefinition(beanClassName, beanDefinition);
                        }
                    }
                }
            }
    
            private static Set<String> getBasePackages(
                    final StandardAnnotationMetadata metadata,
                    final AnnotationAttributes attributes) {
                final String[] basePackages = attributes.getStringArray("basePackages");
                final Set<String> packagesToScan = new LinkedHashSet<>(Arrays.asList(basePackages));
    
                if (packagesToScan.isEmpty()) {
                    // If value attribute is not set, fallback to the package of the annotated class
                    return Collections.singleton(metadata.getIntrospectedClass().getPackage().getName());
                }
    
                return packagesToScan;
            }
        }
    }
    
     类似资料:
    • 问题内容: 我为我写了一个 对于每个我使用以下注释 因此,我决定定义自己的注释女巫,其中包含所有这样的注释 然后,我只用了一个注释 修改后,测试失败 为了使其再次工作,需要我将其添加到 我的问题是为什么我的注释包含注释时不起作用?注释有什么特别之处吗?还是我错过了什么? PS:我使用相同的方法,它们也很好用。 问题答案: 这种机制是Spring框架特有的,在这种机制中,您可以使用本身带有其他注释的

    • 使用 Vim 编辑 Shell 脚本,在进行调试时,需要进行多行的注释,每次都要先切换到输入模式,在行首输入注释符"#"再退回命令模式,非常麻烦。 连续行的注释其实可以用替换命令来完成。换句话说,在指定范围行加"#"注释,可以使用 ":起始行,终止行 s/^/#/g",例如: :1,10s/^/#/g 表示在第 1~10 行行首加"#"注释。"^"意为行首;"g"表示执行替换时不询问确认。如果希望

    • 我正在尝试使用Spring安全注释,如@PreAuthorize和@安全,但我希望评估用户不是在一个角色上,而是他们是否拥有对特定实体的权限,在这种情况下是一家公司。在我的控制器中,我收到一个超文本传输协议请求,其中包含一个firmId作为参数,我想确保这个用户被允许进入这家公司。是否可以使用当前的Spring安全注释?。我正在寻找一个优雅的解决方案,我一直在寻找自定义约束验证器作为jsr303规

    • 在我的Spring Boot项目中,我创建了一个自定义注释,其中validator扩展了ConstraintValidator,以验证RequestBody中的一些字段。注释对于非嵌套字段可以很好地工作,但对于嵌套字段不调用验证器。 我的注释如下所示: 我的验证类: 它在这样的情况下工作正常: 但是当放在嵌套对象上时,不会调用验证器: 类在我的中的用法: 关于如何解决这个问题有什么想法吗?我已经尝

    • 我使用Spring MVC和Hibernate来开发一个Web应用程序。起初,我只使用标准的JSR303注释。然后,我决定使用一些自定义注释来验证一个新用户的用户名和电子邮件的唯一性,该用户希望创建一个帐户。 因此,我创建了两个注释,如下所示(一个用于电子邮件,另一个用于用户名):