Why @Autowired(required=false) do not work on @Configuration beans?

13,844

Solution 1

In addition to the other answers:

The problem is that spring does not take the required=false into account when injecting parameters. See ConstructorResolver

return this.beanFactory.resolveDependency(
        new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter);

The second argument is always true:

public DependencyDescriptor(MethodParameter methodParameter, boolean required)

EDIT: Spring uses the ConstructorResolver for

  • "real" constuctor injection

    @Autowired(required=false) // required=false WILL NOT WORK
    public FooService(Foo foo){
        ...
    }
    
  • factory methods

    @Bean
    @Autowired(required=false) // required=false WILL NOT WORK
    FooService fooService(Foo foo) {
         if (foo == null) {
             return new FooService(new Foo("foo"));
         }
         return new FooService(foo);
    }
    

Thus in both cases the required attribute is ignored.

Solution 2

You have your syntax wrong. The @Autowired(required = false) would need to be relating to the Foo.

For example:

@Configuration
public class SpringContext {

    @Autowired(required = false)
    private Foo foo;

    @Bean
    FooService fooService() {
        if (foo == null) {
            return new FooService(new Foo("foo"));
        }
        return new FooService(foo);
    }
}

Solution 3

Try

@Configuration
public class SpringContext {
//    @Bean
//    Foo foo() {
//        return new Foo("foo");
//    }

    @Autowired(required = false)
    Foo foo;

    @Bean    
    FooService fooService() {
        if (this.foo == null) {
            return new FooService(new Foo("foo"));
        }
        return new FooService(this.foo);
    }
}

Solution 4

required=false does work on @Configuration beans, however, the @Autowired annotation should be placed along with the constructor argument. This is the correct syntax:

@Bean
Test1 test1(@Autowired(required = false) Test2 test2){
    return new Test1(test2);
}
Share:
13,844
Christian Sisti
Author by

Christian Sisti

(your about me is currently blank) click here to edit

Updated on June 30, 2022

Comments

  • Christian Sisti
    Christian Sisti almost 2 years

    Let explain with an example:

    Having this bean:

    public class Foo {
        private String name;
    
        Foo(String name) {
            this.name = name;
        }
    
        public String getName() {
            return this.name;
        }
    }
    

    And this service:

    public class FooService {
        private Foo foo;
    
        FooService(Foo foo) {
            this.foo = foo;
        }
    
        Foo getFoo() {
            return this.foo;
        }
    }
    

    Given the following Spring configuration:

    @Configuration
    public class SpringContext {
    //    @Bean
    //    Foo foo() {
    //        return new Foo("foo");
    //    }
    
        @Bean
        @Autowired(required = false)
        FooService fooService(Foo foo) {
            if (foo == null) {
                return new FooService(new Foo("foo"));
            }
            return new FooService(foo);
        }
    }
    

    For completeness here is a simple unit test:

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = {SpringContext.class})
    public class SpringAppTests {
        @Autowired
        private FooService fooService;
    
        @Test
        public void testGetName() {
            Assert.assertEquals("foo", fooService.getFoo().getName());
        }
    }
    

    Then loading the context will throw a NoSuchBeanDefinitionException (Foo).

    Can anyone see anything wrong/missing on this example, or provide me a reason for that?

    Thank you! Christian