Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does @Required annotation work with JavaConfig?

I'm pretty new to the Spring Framework and I got problems to understand the @Required annotation in combination with a Java configured application.

Here is an example.

Config-File

@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;
    }
}

Context initialization

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

As far as I understood the documentation of the @Required annotation, there should rise an exception, because movie isn't set directly or by autowiring. Instead is the output movie: null.

What am I doing wrong? Or isn't this the correct use of the @Required annotation?

like image 926
a.ha Avatar asked May 27 '13 08:05

a.ha


People also ask

What is the use of @required annotation?

The @Required annotation applies to bean property setter methods and it indicates that the affected bean property must be populated in XML configuration file at configuration time. Otherwise, the container throws a BeanInitializationException exception.

What does @required annotation mean in spring?

The @Required annotation in spring is a method-level annotation used in the setter method of a bean property and therefore making the setter-injection compulsory.

What is @primary annotation in spring boot?

Annotation Type PrimaryIndicates that a bean should be given preference when multiple candidates are qualified to autowire a single-valued dependency. If exactly one 'primary' bean exists among the candidates, it will be the autowired value.

What is JavaConfig in spring boot?

Spring JavaConfig is a product of the Spring community that provides a pure-Java approach to configuring the Spring IoC Container. While JavaConfig aims to be a feature-complete option for configuration, it can be (and often is) used in conjunction with the more well-known XML-based configuration approach.


2 Answers

Setting the required properties in the beans that you are instantiating is your own responsibility. The BeanPostProcessor that processes the bean-definitions in the classes annotated with @Configuration is called ConfigurationClassPostProcessor. The BeanPostProcessor that processes your @Required annotation defaults to RequiredAnnotationBeanPostProcessor, which is registered by default when you use context:annotation-config and context:component-scan in your configuration. If you are not using these two tags, you can even register your own RequiredAnnotationBeanPostProcessor as a bean.

Now, the default implementation of the RequiredAnnotationBeanPostProcessor has a method called boolean shouldSkip(..) that checks for a boolean attribute named SKIP_REQUIRED_CHECK_ATTRIBUTE. The value of this attribute is checked for each bean during the post-processing by the RequiredAnnotationBeanPostProcessor. If it returns false, the @Required constraint is enforced, otherwise it is not.

Now, the ConfigurationClassPostProcessor set the value of this attribute to true while creating the bean definitions from the @Configuration classes (I guess for the reason that if you are defining a bean, you should ensure that it has the required properties). Hence, the @Required is not enforced for such beans.

As an aside, You might think that where did this SKIP_REQUIRED_CHECK_ATTRIBUTE attribute come from and where is it set: it is set on the instances of BeanDefinition that are used by Spring internally for bean creation and post-processing.

If you really want to enforce the @Required constraints, you would have to override the RequiredAnnotationBeanPostProcessor, override the boolean shouldSkip(..) method and register this class instead of the default RequiredAnnotationBeanPostProcessor. And as the documentation for RequiredAnnotationBeanPostProcessor says:

A default RequiredAnnotationBeanPostProcessor will be registered by the "context:annotation-config" and "context:component-scan" XML tags. Remove or turn off the default annotation configuration there if you intend to specify a custom RequiredAnnotationBeanPostProcessor bean definition.

Another way would be to use the initMethod attribute on your @Bean annotation. Which could perform checks to see that the required properties are indeed set. However, since this is code based configuration, you could just as well call that init method yourself.

Also, in my opinion, there is not much point in going through a lot of trouble to use your own RequiredAnnotationBeanPostProcessor, as the following documentation says:

Please note that an 'init' method may still need to implemented (and may still be desirable), because all that this class does is enforce that a 'required' property has actually been configured with a value. It does not check anything else... In particular, it does not check that a configured value is not null.

So, to summarize: @Required doesn't work with @Configuration classes by default. If you need to make sure that all your properties are set, you can just as well do it yourself when you create the bean in the @Bean methods (By calling some init method that performs such validations, or just supplying the required properties yourself). And if you really need to make the @Required annotation work, you'd need to use your own implementation of the RequiredAnnotationBeanPostProcessor, register it as a bean in the spring context and give up the benefits of context:annotation-config.

like image 183
Bhashit Parikh Avatar answered Sep 28 '22 08:09

Bhashit Parikh


Just tried to declare a @Bean RequiredAnnotationBeanPostProcessor with overridden shouldSkip() method.

Yes, it checks my beans, but it fails even if I set all the required properties, i.e. it always fails.
I think Spring has a real problem with supporting @Required annotation for Java Config, since Spring has no way to tell whether or not you have set the property when you do it directly in Java code. (It can't inspect for 'null' fields later, since this would mean changing the semantics of the @Required annotation which should allow explicitly set null values).

When you use an XML config, Spring creates a wrapper object to set the properties, so it can track all the configured 'setXxx()' operations.

Conclusion: there is no reasonable way to enable @Required annotation for beans created in Java @Configuration classes.
(Very unfortunate feature, in my opinion, since the bean class writer and the class user might be different persons).

like image 36
Alexander Avatar answered Sep 28 '22 08:09

Alexander