【问题标题】:How does @Required annotation work with JavaConfig?@Required 注解如何与 JavaConfig 一起使用?
【发布时间】:2013-05-22 02:02:24
【问题描述】:

我是 Spring 框架的新手,在理解 @Required 注释与 Java 配置的应用程序时遇到了问题。

这是一个例子。

配置文件

@Configuration
public class AppConfig {
    @Bean
    public Movie movieA() {
        return new Movie();
    }

    @Bean
    public MovieHolder holder() {
        return new MovieHolder();
    }
}

MovieHolder.java

public class MovieHolder {

    private Movie movie;

    public Movie getMovie() {
        return movie;
    }

    @Required
    public void setMovie(Movie movie) {
        this.movie = movie;
    }
}

上下文初始化

ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
MovieHolder holder = (MovieHolder) context.getBean("holder");
System.out.println("movie: " + holder.getMovie());

据我了解@Required 注释的文档,应该会出现异常,因为电影不是直接设置或通过自动装配设置的。而是输出movie: null

我做错了什么?或者这不是@Required注解的正确用法吗?

【问题讨论】:

    标签: spring annotations required


    【解决方案1】:

    刚刚尝试用重写的 shouldSkip() 方法声明一个 @Bean RequiredAnnotationBeanPostProcessor

    是的,它会检查我的 bean,但即使我设置了所有必需的属性,它也会失败,即它总是失败。
    我认为 Spring 在支持 Java Config 的 @Required 注释方面存在真正的问题,因为当您直接在 Java 代码中执行此操作时,Spring 无法判断您是否设置了该属性。 (它不能在以后检查 'null' 字段,因为这意味着更改应该允许显式设置空值的 @Required 注释的语义)。

    当您使用 XML 配置时,Spring 会创建一个包装器对象来设置属性,因此它可以跟踪所有已配置的“setXxx()”操作。

    结论:没有合理的方法可以为 Java @Configuration 类中创建的 bean 启用 @Required 注释。
    (在我看来,这是一个非常不幸的特性,因为 bean 类编写者和类用户可能是不同的人)。

    【讨论】:

      【解决方案2】:

      在您正在实例化的 bean 中设置所需的属性是您自己的责任。处理带有@Configuration 注释的类中的bean 定义的BeanPostProcessor 称为ConfigurationClassPostProcessor。处理您的@Required 注释的BeanPostProcessor 默认为RequiredAnnotationBeanPostProcessor,当您在配置中使用context:annotation-configcontext:component-scan 时默认注册。如果你不使用这两个标签,你甚至可以将自己的RequiredAnnotationBeanPostProcessor注册为bean

      现在,RequiredAnnotationBeanPostProcessor 的默认实现有一个名为 boolean shouldSkip(..) 的方法,用于检查名为 SKIP_REQUIRED_CHECK_ATTRIBUTE 的布尔属性。在后处理期间,RequiredAnnotationBeanPostProcessor 会检查每个 bean 的此属性的值。如果返回false,则强制执行@Required约束,否则不执行。

      现在,ConfigurationClassPostProcessor 在从 @Configuration 类创建 bean 定义时将此属性的值设置为 true(我猜是因为如果 正在定义一个 bean ,应该确保它具有所需的属性)。因此,@Required 不会对此类 bean 强制执行。

      顺便说一句,您可能会认为这个SKIP_REQUIRED_CHECK_ATTRIBUTE 属性来自哪里以及它在哪里设置:它是在BeanDefinition 的实例上设置的,Spring 在内部用于bean 创建和后处理。

      如果您真的想强制执行@Required 约束,则必须覆盖RequiredAnnotationBeanPostProcessor,覆盖boolean shouldSkip(..) 方法并注册此类而不是默认的RequiredAnnotationBeanPostProcessor。正如RequiredAnnotationBeanPostProcessor 的文档所说:

      默认的RequiredAnnotationBeanPostProcessor 将由“context:annotation-config”和“context:component-scan”XML 标签注册。如果您打算指定自定义的RequiredAnnotationBeanPostProcessor bean 定义,请删除或关闭默认注释配置。

      另一种方法是在 @Bean 注释上使用 initMethod 属性。它可以执行检查以查看是否确实设置了所需的属性。但是,由于这是基于代码的配置,您也可以自己调用 init 方法。

      另外,在我看来,使用您自己的RequiredAnnotationBeanPostProcessor 并没有多大意义,正如以下文档所述:

      请注意,'init' 方法可能仍需要实现(并且可能仍然是可取的),因为该类所做的只是强制“必需”属性实际上已配置了一个值。它不检查任何其他内容...特别是,它不检查配置的值是否不为空。

      所以,总结一下: @Required 默认情况下不适用于 @Configuration 类。如果您需要确保设置了所有属性,您也可以在 @Bean 方法中创建 bean 时自己完成(通过调用一些执行此类验证的 init 方法,或者仅提供所需的自己的属性)。如果您真的需要使@Required 注释工作,您需要使用自己的RequiredAnnotationBeanPostProcessor 实现,将其注册为spring 上下文中的bean 并放弃好处context:annotation-config.

      【讨论】:

      • 嗯,感谢您的解决方案,我会试一试。但我不明白为什么这么复杂?在使用 Java 中定义的配置时,是否有任何理由不使用 @Required-Annotation?
      • 如果你真的需要使用它。它有一些注意事项(失去了context:annotation-config 的好处),无论如何使用它并没有太大的好处。不过也不是很复杂。官方文档有很大帮助,如果这不起作用,您可以浏览源代码。您可能会使用@Required 来确保某些属性具有有效值,而@Required 注释不是这样做的。如果您需要这样做,只需在您的 @Bean 方法中检查它,或者按照答案中的说明实现 initMethod
      • 这不是重点,我一定需要@Required-Tag。当使用基于 java 的配置时,我仍然感到惊讶,这个标签总是被忽略。在ConfigurationClassBeanDefinitionReader 中,我发现以下行:beanDef.setAttribute(RequiredAnnotationBeanPostProcessor.SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE); 这就是为什么总是跳过所需检查的原因。
      • 是的,这就是我的回答。考虑到 bean-creation 是由您执行的,这并不奇怪。这有点道理。如果您需要确保某些内容,只需在创建 bean 时执行此操作。虽然编辑了答案,但我在一个地方输入了false 而不是true
      • 啊,好吧。现在对我来说很明显,你的意思是什么。 - 谢谢!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-02
      • 1970-01-01
      • 2018-09-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多