springautowiredconstructor-injection

Spring :how to avoid combining constructor injection with autowired fields


What would be the best alternative to avoid having both constructor and field injections?

As I understand, field injections are not recommended and constructor is the way to go. (http://olivergierke.de/2013/11/why-field-injection-is-evil/)

Please find here below where class A is being created inside @Configuration bean and class B is created as a constructor param in class A.

However, class A needs other beans also to operate such as C/D/E which in turn has got their own autowired dependencies. I'm confused how to apply best recommended approach here.

Any suggestions, please let me know.

@Configuration 
class Config {
    @Bean
    public A create_A() {
        A = new A(new B());
    }
}

class A {
    B b;

    @Autowired
    C c;

    @Autowired
    D d;

    @Autowired
    E e;


    A(B b) {
        this. b = b;
    }

}


class C {

    @Autowired 
    Y y;  
}

Solution

  • In general @Autowired annotation has appeared in Spring 2.5 and later on in Spring 3.0 they've introduced "Java DSL" for configuration, the way of configuration with @Configuration annotation and @Bean annotated method.

    If you work with @Configuration like in your example of class A, then you don't really have to use @Autowired. You also don't need annotations like @Component if you manage everything via @Configuration.

    So, assuming you doubt how to inject classes like B or C into class A without autowired assuming the class A is defined via @Configuration, you can do the following:

    @AllArgsConstructor // I've used lombok - it will create a constructor with B and C for you, but you can define the constructor in java as well
    public class A {
       private final B b; 
       private final C c;
       ...
    }
    
    
    @Configuration
    public class MyConfiguration {
       @Bean
       public A a(B b, C c) {
           return new A(b,c);
       }
    
       @Bean
       public B b() {
          return new B();
       }
    
       @Bean
       public C c() {
          return new C();
       }
    
    }
    

    Another alternative is using explicit method calls in the @Configuration:

    @Configuration
    public class MyConfiguration {
       @Bean
       public A a() {  // note, as opposed to the previous example, no parameters here
           return new A(b(),c()); // note the explicit calls
       }
    
       @Bean
       public B b() {
          return new B();
       }
    
       @Bean
       public C c() {
          return new C();
       }
    
    }
    

    This method won't work however if B and/or C were defined in the different @Configuration file or via @Component annotation

    Note, that class A doesn't have any Autowired fields, everything is injected via the constructor. Basically class A now doesn't have any spring related annotations at all.

    Now, if you don't use configurations for B and C, for example, you still can put @Component on them and this will still work, because Spring "gathers" the information about what beans should be created and it has multiple resources to "obtain" this kind of information: via class scanning of classes annotated with @Component, via @Bean-s defined in the configuration, and so forth.

    Even if you don't manage the class A the constructor injection will still work:

    @Component
    @AllArgsConstructor // if there is only one constructor in the class, spring will use it for autowiring automatically, so no need to put @Autowired even on the constructor for recent spring versions
    class A {
      private final B b;
      private final C c;
    }
    
    @Component
    class B {
    }
    
    @Component 
    class C {
    }