I updated the Java coding artifact IDEA these days. After the upgrade, I started daily development. Maybe the IDEA version I used before was old, but AFTER the upgrade, I found a warning in the daily writing method before. See figure:As shown in the figure above, which is strange, the business layer we often write about isserviceThe interface layer and the corresponding implementation class layer use annotations to inject properties. This is alsospringIOCTo provide a more convenient place. I use the autofix hint provided by IDEA to fix it and then inject it in the form of constructors.

But years of Spring development have taught me that using the @AutoWired annotation for dependency injection is definitely fine. But my code cleanliness freaks don’t allow me to leave a warning here so unambiguously. So, with my neat fetish, and my curiosity, I began to study this warning.

Warning content

Here’s a quick translation of what the autoprompt means:

It is not recommended to do dependency injection directly on fields.

The Spring development team recommends always using constructors for dependency injection in Java beans. For required dependencies, always use assertions to confirm them.

Before we address the above, let’s review a few Spring-related issues:

Dependency injection

Spring has three approaches to dependency injection

  1. Property-based injection

This type of injection is dependency injection using annotations on bean variables. It is essentially injected directly into the field by reflection. This is one of the most familiar approaches I see in development.

@Autowired
PushTaskService pushTaskService;
Copy the code
  1. Setter-based injection

Dependency injection is done by using the setXXX() method of the corresponding variable and using annotations on the method. Such as:

private static TaskGroupTemplateRepository taskGroupTemplateRepository;
private static TaskGroupService taskGroupService;

@Autowired
public void setTaskGroupTemplateRepository(TaskGroupTemplateRepository taskGroupTemplateRepository,TaskGroupService taskGroupService){
    ExcelListener2.taskGroupTemplateRepository = taskGroupTemplateRepository;
    ExcelListener2.taskGroupService = taskGroupService;
}
Copy the code

Note: In Spring 4.5 and later, the @autowired annotation above setXXX is optional.

  1. Construction-based injection

Constructor based injection is done by placing all required dependencies in parameters with annotated constructors and initializing the corresponding variables in the constructor. Such as:

@Autowired
public ExcelListener(@Qualifier("taskGroupService") TaskGroupService taskGroupService) {
    this.taskGroupService = taskGroupService;
}
Copy the code

What does @autowired do

The @autoWired annotation is used when we need to Inject properties. There are two other annotations, @Resource and @Inject. Spring supports dependency injection using @autoWired, @Resource, and @Inject annotations. Let’s see what the difference is:

The @autowired annotation for the Spring framework is, understandably, Spring’s own son. Here is an example code

public interface IndexService {

    void sayHello(a);
}

@Service
public class IndexServiceImpl implements IndexService {

    @Override
    public void sayHello(a) {
        System.out.println("hello, this is IndexServiceImpl"); }}@Service
public class IndexServiceImpl2 implements IndexService {

    @Override
    public void sayHello(a) {
        System.out.println("hello, this is IndexServiceImpl2"); }}Copy the code

The test method

@SpringBootTest
public class Stest {

    @Autowired
    // @Qualifier("indexServiceImpl2")
    IndexService indexService;

    @Test
    void gooo(a) { Assertions.assertNotNull(indexService); indexService.sayHello(); }}Copy the code

Let’s explain the process for matching beans:

  1. Find matches in the context by type, find beans of Type IndexService
  2. If there are multiple beans, they are matched by name
    • If there is an @Qualifier annotation, the name specified by @Qualifier is matched to find the bean whose name is indexServiceImpl2
    • If not, match by variable name. Find the bean with name indexService
  3. If no match is found, an error is reported. (@autowired (Required =false), if set to False (the default is true), no exception will be thrown if the injection fails)

What does @Inject do

Under the environment of Spring, @ Inject and @autowired is the same, the dependency injection because they are using AutowiredAnnotationBeanPostProcessor this post processor to process.The difference between the two is that @Inject is in the Java EE package and needs to be introduced separately in the SE environment. Another difference is that @AutoWired can set Required =false while @Inject does not. Some say @Inject is Spring’s godson.

What does @resource do

@resource is an annotation of the JSR-250 definition. Spring in CommonAnnotationBeanPostProcessor implements the handling of JSR – 250 annotations, including @ the Resource. This @Resource has two attributes name and type. In Spring, the name attribute is defined as the bean name, and type is the type of the bean. If the attribute is annotated with @resource, its injection flow looks like this:

  1. If both name and Type are specified, a unique matching bean is found from the Spring context and assembled, failing which an exception is thrown.
  2. If name is specified, the bean with a matching name is searched from the context for assembly, and an exception is thrown if no bean is found.
  3. If type is specified, an exception will be thrown if a unique bean whose type matches is found in the context and assembled.
  4. If neither name nor type is specified, byName is used for assembly by default. If no match is found, byType is used for assembly.

IDEA has a warning. Field injection is not recommended. Warning injection is the first way to inject using property annotations.

Property injection advantages

The code looks simple and easy to understand. Your classes can focus on the business without being tainted by dependency injection. All you have to do is throw @AutoWired on top of the variable so that developers can write code.

Possible problems with property injection
  • Question 1

Field-based injection, while not absolutely forbidden, can bring some implicit problems. Let’s take an example:

@Autowired
private Person person;

private String company;

public UserServiceImpl(a){
    this.company = person.getCompany();
}
Copy the code

The Person class will be injected as a dependency into the current class, and the company property of the class will be initialized using the Person.getCompany () method. If we try to run it, we’ll see that we got an error. A similar question has been asked on StackOverflow.

Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [...] : Constructor threw exception; nested exception is java.lang.NullPointerExceptionCopy the code

This.pany = person.getCompany(); This code has a null pointer. The reason for this problem is that Java initializes a class in the order of static variables or static statement blocks — > instance variables or initialization statement blocks — > constructors — > @AutoWired. So when the constructor of this class is executed, the Person object has not yet been injected and its value is still null.

  • Question 2

Using this way based on the field injection, add dependent, it is very simple, even if your class have a dozen rely on no questions, you may feel if you have a class with a lot of other objects, have too much reliance on usually means that your class to take more responsibility, clear violation of the principle of single responsibility. I took a look at our current business code by the way and this problem is really common in our project code.

  • Question 3

This form of injection means that your classes cannot be instantiated without reflection (for example, during unit testing) and must be instantiated through a dependency container. That is, classes and dependent containers are strongly coupled and cannot be used outside the container.

Spring advice

Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies.

It basically means two sentences

Mandatory dependencies are constructors, and mutable dependencies are setters

Spring’s instructions for using constructor injection

The Spring team advocates construction-based injection because it allows you to inject dependencies into immutable variables while ensuring that the values of those variables are not null. In addition, components that have been constructively injected (e.g., services) can be guaranteed to be fully ready when called. At the same time, from a code quality perspective, a large constructor usually represents a code structure problem, and the class may be taking on too much responsibility.

Spring’s instructions for using setter methods for injection

Setter-based injection should only be used to inject non-essential dependencies and should provide a reasonable default value for this dependency in the class. If you use setters to inject the necessary dependencies, you will have too many null checks flooding your code. One advantage of using setter injection is that the dependency can be easily changed or re-injected.

So we should be careful in the daily development process often a warning can be involved in a knowledge point.

Author’s note: Welcome to pay attention to the author’s official account, regularly share IT Internet, finance, education and other work experience, life experience, welcome to exchange.