Field injection is not recommended (field injection is no longer recommended) prohibit the use of @Autowired annotations in the project?

Field injection is not recommended (field injection is no longer recommended)

1. Description

spring framerwork 3.0Recently, the company upgraded the framework from the original one 5.0, and then suddenly found that idea gave a warning prompt on the **@Autowired** annotation of property injection when writing the code, like the following, which is quite confusing, after all, I have been writing for many years.

Field injection is not recommended

img

After consulting the relevant documents, it turns out that this prompt spring framerwork 4.0began to appear in the future. Since spring 4.0, property injection is not recommended, and constructor injection and setter injection are recommended instead.

The following will show the different types of dependency injection that the spring framework can use, and the applicable situations of each type of dependency injection.

2. Types of dependency injection

Although the documentationspring framerwork 5.1.3 against defines only two main types of dependency injection, there are actually three;

  • Constructor-Based Dependency Injection
  • Setter-based dependency injection
  • Field-Based Dependency Injection

It 基于字段的依赖注入is widely used, but idea or other static code analysis tools will give prompt information, which is not recommended.

You can even see this injection method in some official Spring guides:

insert image description here

2.1 Constructor-based dependency injection

In constructor-based dependency injection, the class constructor is annotated with @Autowiredand contains a number of parameters related to the object to be injected.

@Component
public class ConstructorBasedInjection {
    
    

    private final InjectedBean injectedBean;

    @Autowired
    public ConstructorBasedInjection(InjectedBean injectedBean) {
    
    
        this.injectedBean = injectedBean;
    }
}

Then in the official spring documentation , the @Autowired annotation can also be omitted.

public class SimpleMovieLister {
    
    

    // the SimpleMovieLister has a dependency on a MovieFinder
    private MovieFinder movieFinder;

    // a constructor so that the Spring container can inject a MovieFinder
    public SimpleMovieLister(MovieFinder movieFinder) {
    
    
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
}

The main advantage of constructor-based injection is that you can declare fields that need to be injected as final so that they will be initialized during class instantiation, which is handy for required dependencies.

2.2 Setter-based dependency injection

In setter-based dependency injection, the setter method is marked as **@Autowired**. Once a bean is instantiated using a parameterless constructor or a parameterless static factory method, these setter methods are called by the Spring container in order to inject the bean's dependencies.

@Component
public class SetterBasedInjection {
    
    

    private InjectedBean injectedBean;

    @Autowired
    public void setInjectedBean(InjectedBean injectedBean) {
    
    
        this.injectedBean = injectedBean;
    }
}

Like constructor-based dependency injection, in the official documentation , **@Autowired** in Setter-based dependency injection can also be omitted.

public class SimpleMovieLister {
    
    

    // the SimpleMovieLister has a dependency on the MovieFinder
    private MovieFinder movieFinder;

    // a setter method so that the Spring container can inject a MovieFinder
    public void setMovieFinder(MovieFinder movieFinder) {
    
    
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
}

2.3 Attribute-Based Dependency Injection

In property-based dependency injection, fields/properties are marked with **@Autowired**. The Spring container will set these fields once the class is instantiated.

@Component
public class FieldBasedInjection {
    
    
    @Autowired
    private InjectedBean injectedBean;
}

As you can see, this is the cleanest approach to dependency injection, as it avoids adding boilerplate code and does not require declaring a constructor for the class. The code looks clean and concise, but as the code inspector already hinted to us, this approach has some drawbacks.

3. Field-based dependency injection flaws

3.1 Immutable fields are not allowed

Field-based dependency injection doesn't work on fields declared final/immutable because those fields must be instantiated when the class is instantiated. The only way to declare immutable dependencies is to use constructor-based dependency injection.

3.2 It is easy to violate the single responsibility design principle

In object-oriented programming, the five design principles of SOLID are widely used (generally six design principles in China) to improve code reusability, readability, reliability and maintainability

S stands for the single responsibility principle in SOLID , that is, a class should only be responsible for one responsibility, and all services provided by this class should only serve the responsibility it is responsible for.

Using field-based dependency injection, frequently used classes will gradually add more and more dependencies to the class over time. We are very happy with it, and it is easy to ignore that there are too many dependencies in the class. . But if you use constructor-based dependency injection, as more and more dependencies are added to the class, the constructor will become larger and larger, and we can detect something wrong at a glance.

Having a constructor with more than 10 parameters is a clear signal that the class has transformed into a large and comprehensive collection of functions, and the class needs to be broken into smaller, more maintainable chunks.

So, while property injection is not the direct cause of breaking the Single Responsibility Principle, it hides the signals and makes them easy to ignore.

3.3 Tight coupling with dependency injection container

The main reason to use field-based dependency injection is to avoid boilerplate code for getters and setters or to create constructors for classes. In the end, this means that the only way to set these fields is to instantiate the classes through the Spring container and inject them using reflection, otherwise the fields will remain null.

The Dependency Injection design pattern decouples the creation of class dependencies from the class itself and transfers this responsibility to the class injection container, thus allowing decoupling of program design and following the Single Responsibility and Dependency Inversion principles (which are equally reliable). Thus, the decoupling of classes achieved by autowiring fields ends up being lost by being coupled again with the class injection container (Spring in this case), rendering the class useless outside of the Spring container.

This means that if you want to use your class outside of the application container, e.g. for unit testing, you will be forced to use the Spring container to instantiate your class as there is no other possible way (besides reflection) to set Autowire fields.

3.4 Hiding dependencies

When using dependency injection, the affected classes should expose these dependencies clearly using a public interface, either by exposing required dependencies in the constructor, or by exposing optional dependencies using methods (setters). When using field-based dependency injection, these dependencies are essentially hidden from the outside world.

4. Summary

We've seen that field-based injection should be avoided as much as possible because it has many disadvantages, no matter how elegant it may seem. The recommended approach is to use constructor-based and setter-based dependency injection. For required dependencies, it is recommended to use constructor-based injection, make them immutable, and prevent them from being null. For optional dependencies, sett-based injection is recommended.

5. Reference documents

Field injection is not recommended – Spring IOC by Marc Nuri

spring官方文档 1.4. Dependencies?docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#beans-factory-collaborators

6. Update questions

  1. Regarding @Autowirethe problem that setter-based DI annotations can be omitted,
    I took it for granted in the previous article, and @Autowirethe specific use cases can be found here

Core Technologies?docs.spring.io/spring-framework/docs/5.2.3.RELEASE/spring-framework-reference/core.html#beans-required-annotationimg

insert image description here

The general idea is:
starting from Spring Framework 4.3, if the target bean only defines a constructor, then the @Autowired annotation is no longer required on such a constructor. However, if several constructors are available, at least one constructor must be annotated with @Autowired in order to instruct the container which constructor to use.

2. Recently, I found a pit in use. After changing it to constructor injection, if you want to mock, it is simply a big pit. If you want to mock in the company, you need to be cautious in the transformation. . .

Guess you like

Origin blog.csdn.net/qq_43842093/article/details/132642094