Search code examples
javaspringspring-annotations

Why is @Required ignored by Spring?


I've read that @Required uses to make sure the property has been set. But when I try to use it along with Spring Annotation Configuration it doesn't work.

Below you can familiarize with my code base.

@Configuration
@ComponentScan
public class AppConfig {

    @Bean(initMethod = "initMethod")
    public SimpleClass simpleClass(){
        return new SimpleClass();
    }

}

public class SimpleClass implements InitializingBean {

    private int n;

    public SimpleClass() {
        System.out.println("constructor");
    }

    public int getN() {
        return n;
    }

    @Required
    public void setN(int n) {
        System.out.println("setter");
        this.n = n;
    }

    void initMethod(){

    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet()");
    }

}

public class Main {

    public static void main(String[] args) {

        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        SimpleClass simpleClass = context.getBean(SimpleClass.class);

    }

}

Why does Spring application context create the SimpleClass Bean and don't complain about the absence of injection via setter?

UPD: When I try to do the same using XML configuration and add then I receive a "Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Property 'n' is required for bean 'simple'"


Solution

  • @Required documentation states (emphasis is mine) :

    Marks a method (typically a JavaBean setter method) as being 'required': that is, the setter method must be configured to be dependency-injected with a value.

    With Spring in order to configure a method as dependency injected you have to specify it (@Autowired is the standard way).

    But specifying both @Autowired and @Required on a method seems clumsy today :

    @Autowired
    @Required
    public void setN(int n) {
        System.out.println("setter");
        this.n = n;
    }
    

    Instead, to configure the setter to be both dependency-injected and required I advise to use only @Autowired that by default is required as you can notice :

    public @interface Autowired {
    
        /**
         * Declares whether the annotated dependency is required.
         * <p>Defaults to {@code true}.
         */
        boolean required() default true;
    
    }
    

    So it is enough :

    @Autowired    
    public void setN(int n) {
        System.out.println("setter");
        this.n = n;
    }
    

    As a side note, the setter injection will probably fail as the int n will probably not be resolved as a dependency. The @Value annotation on the parameter could probably help you.