Spring Dependency Injection

Let’s take a look at a few of Spring’s dependency injection methods: constructor injection, Field injection and Setter injection.

Constructor injection

Injects the dependent object into the dependency via the constructor parameters and at the time of initializing the object.

Spring’s recommended injection method for mandatory dependency usage.

1
2
3
4
5
6
7
8
9
private DependencyA dependencyA;
private DependencyB dependencyB;;

// @Autowired is not required, a new feature was added in Spring 4.x: if the class only provides a constructor with parameters, you don't need to write @Autowired annotations to its internal properties, Spring will automatically inject the properties for you.
@Autowired
public DI(DependencyA dependencyA, DependencyB dependencyB) {
    this.dependencyA = dependencyA;
    this.dependencyB = dependencyB;
}
  • Pros: You can get a usable object after object initialization, and unit tests can be instantiated without starting a DI container using Mock.
  • Disadvantages: When there are many objects to be injected, the constructor parameter list will be long; not flexible enough. If there are multiple injection methods and each method only needs to inject a few specified dependencies, then you need to provide multiple overloaded constructors.

Setter Injection

The IoC Service Provider injects the dependent object into the dependent class by calling the setter function provided by the member variable. Setter injection is used if there are optional mutable dependencies, and optional dependencies can be specified with @Autowired(required = false). Constructor injection cannot do this, as it is applied to all constructors.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
private DependencyA dependencyA;
private DependencyB dependencyB;

@Autowired
public void setDependencyA(DependencyA dependencyA) {
    this.dependencyA = dependencyA;
}

@Autowired(required = false)
public void setDependencyB(DependencyB dependencyB) {
    this.dependencyB = dependencyB;
}

setter injection is more flexible and can selectively inject the required objects.

Field injection

Property injection, using annotations on bean variables for dependency injection, is essentially injecting directly into the field through reflection, which should be one of the most common methods seen in development.

1
2
3
4
5
@Autowired
private DependencyA dependencyA;

@Autowired
private DependencyB dependencyB;

Filed injection is the easiest and most convenient way.

Limitations and alternatives to Field injection

Limitations

In the past Field injection should be our most used dependency injection method, directly introduce the bean and then use @Autowired on the variables on the line, but later you will find in IDEA will give a warning “Field injection is not recommended”.

Because Field injection is easy to use, we intentionally or unintentionally introduce many dependencies, and when injecting too many dependencies means that the class takes on too much responsibility, which violates the object-oriented single responsibility principle, and there is no warning no matter how many are introduced, because this approach can be extended indefinitely.

However, Field injection also has the limitation of not being able to use the @Autowired annotation on fields declared as final/immutable, since these fields must be initialized when the class is instantiated.

Furthermore, Field injection is not unit test friendly, you have to use the Spring IoC container to create these beans (and IoC container strong coupling), but unit testing in principle to be fast, start the IoC container is too slow, if it is construction injection, we can complete the bean as a normal class to create objects, just pass directly through the construction.

Alternatives

If we don’t use @Autowired, we can also use the @Resource annotation provided by the JDK, which reduces coupling with Spring and is just as easy to use, so it’s just as easy to abuse.

The more recommended way is to use construct injection. When more dependencies are needed, the more constructor parameters look ugly and we can use Lombok to simplify constructor injection.

Lombok provides three related annotations to simplify dependency injection.

  • @AllArgsConstructor used to generate constructor methods that contain all field constructs.
  • @NoArgsConstructor used to generate constructor methods with no parameters.
  • @RequiredArgsConstructor generates constructor methods that contain only fields declared as final or non-null.

Then you will find that the use becomes almost as simple as Field injection. You want to prevent abuse or you have to control it yourself and design the class well to avoid including too many responsibilities.

Reference https://zguishen.com/posts/c5bbdc80.html