techsavvy
techsavvy

Reputation: 143

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

Upvotes: 1

Views: 2478

Answers (3)

Mark Bramnik
Mark Bramnik

Reputation: 42541

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 {
}

Upvotes: 2

Jalil.Jarjanazy
Jalil.Jarjanazy

Reputation: 871

@Bernd answered the question really but I thought to include some code to make it more clear

@Component
class B{
    // what ever it is
}

@Component
class A {

private final B b;
private final C c;
private final D d;
private final E e;

@Autowired
A(B b, C c, D d, E e) {
    this.b= b;
    this.c = c;
    this.d = d;
    this.e = e;
    }

}

@Component
class Y{
    // what ever it is
}

@Component
class C{
private final Y y;

@Autowired
C(Y y){
    this.y = y;
}

Upvotes: 2

Bernd Farka
Bernd Farka

Reputation: 512

Your Example is a bit hard to follow since it is missing context.

Overall said: Field Injection is BAD on Singelton Components like Repository, Service or Controller.

Somehow your example is a bit hard to follow, one Pattern which is working for me:

  • make the fields which should be injected via the C'tor final
  • annotate the classes with the corresponding Spring Stereotype (@Component, @Service...)

so you have to take care of those fields and even the compiler is telling you if something goes wrong...

Upvotes: 1

Related Questions