2023.11.7 Three major methods of Spring dependency injection

Table of Contents

Foreword

Property injection (@Autowired)

Setter injection

Constructor injection

@Resource

The difference between @Autowired and @Resource

The difference between @Autowired and @Resource in finding Bean objects


Foreword

Profile

?
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:content="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/ context https://www.springframework.org/schema/context/spring-context.xsd">
    <!--base-package indicates the path to be scanned-->
    <content:component-scan base-package="com.java.demo"></content:component-scan>
</beans>

?

Startup class

import com.java.demo.controller.StudentController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
// Get the Spring context object
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// Get the Bean object
        StudentController studentController = context.getBean("studentController",StudentController.class);
        studentController.sayHi();
    }
}
  • In the startup class, the following three injection methods cannot be used to inject objects
  • When running the startup class, the main method needs to be executed. However, the main method is modified by the static keyword, that is, the main method is a static method
  • The loading order of static methods is higher than Spring container initialization
  • So in this static method, we cannot use the following three dependency injection methods to obtain the Bean object
  • So when we give examples below to implement these three dependency injection methods, we will still obtain the Bean object of StudentController through the startup class
  • But the following three dependency injection methods will be used in the StudentController class to successfully inject the Bean object of the UserService class in the StudentController class

Attribute injection (@Autowired)

  • In daily development, attribute injection is our most commonly used injection method

Example

import com.java.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

// Use @Controller annotation to store the current class in the spring container
@Controller
public class StudentController {

    @Autowired // Inject the Bean object of UserService
    private UserService userService;

    public void sayHi() {
        System.out.println("student controller say hi");
// Directly call the member method that executes userService
        userService.sayHi();
    }
}

Results of running startup class:

Key understanding

When using property injection:

  • When the Spring container starts, the ApplicationContext instance will be instantiated and initialized. It is responsible for loading configuration files (such as XML files) and creating and managing all Bean objects
  • Then when the container creates the bean object of the StudentController class, it will automatically detect the dependencies of the StudentController class and inject the corresponding dependency objects
  • The Bean object of UserService is automatically injected into the StudentController class by the Spring container at this time
  • That is, the userService object in the StudentController class is directly assigned by the Spring container

Advantages

  • Simple to implement and easy to use
  • Just add an annotation (@Autowired) to the variable to get the injected Bean object

Disadvantages

1. Final modified variable injection cannot be implemented

  • Variables modified by the final keyword in Java are called constants
  • The declaration and initialization of constants need to be completed at the same time and can only be assigned once
  • Method 1: Direct assignment
  • Method 2: Assign value through constructor method

2. Only applicable to IoC containers (compatibility issues)

  • If the attribute injection code is ported to other non-IoC frameworks, the code will be invalid, so its compatibility is limited

3. Because the writing method is simple, the probability of violating the single responsibility principle is greater (risk exists)

  • The single responsibility principle is an important principle in object-oriented design. It states that a class should have one and only one reason for its change
  • What is emphasized here is the possibility of violating the Single Responsibility Principle, rather than the certain violation of the Single Responsibility Principle, which is strongly related to the programmer’s own code

Setter injection

Example

import com.java.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

// Use @Controller annotation to store the current class in the spring container
@Controller
public class StudentController {

    // Inject using Setter
    private UserService userService;

    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public void sayHi() {
        System.out.println("student controller say hi");
// Directly call the member method that executes userService
        userService.sayHi();
    }
}
  • You need to use @Autowired to add to the Setter method
  • It means that Spring will assign corresponding values to the parameters of the Setter method

Execution results of running startup class:

Key understanding

When using Setter injection:

  • When the Spring container starts, the ApplicationContext instance will be instantiated and initialized. It is responsible for loading configuration files (such as XML files) and creating and managing all Bean objects
  • Then when the container creates the bean object of the StudentController class, it will automatically detect the dependencies of the StudentController class and inject the corresponding dependency objects
  • The Bean object of UserService is automatically injected into the StudentController class by the Spring container at this time
  • That is, the parameters of the setUserService method in the StudentController class are directly assigned by the Spring container
  • Then pass the this.userService = userService statement in the method
  • Pass the userService object assigned by the Spring container to the userService variable in the StudentController class
  • At this point the StudentController class can successfully use the Bean object of the UserService class

Disadvantages

1. It is also impossible to implement final modified variable injection

2. The injected object can be modified

import com.java.demo.controller.StudentController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
// Get the Spring context object
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// Get the Bean object
        StudentController studentController = context.getBean("studentController",StudentController.class);
// setUserService can be called at will, and then change the userService Bean object that has been injected by the Spring container
// Here directly change the value of the userService variable in the studentController object to null
        studentController.setUserService(null);
// An error will occur when executing here
        studentController.sayHi();
    }
}

Run results:

Illustrated reason:

Constructor injection

  • Constructor method injection is the officially recommended injection method by Spring
  • But the most commonly used method in daily development is attribute injection

Features:

  • If there is only one constructor in the current class, the @Autowired annotation can be omitted

Examples

import com.java.demo.service.UserService;
import org.springframework.stereotype.Controller;

// Use @Controller annotation to store the current class in the spring container
@Controller
public class StudentController {

    // Use the constructor to inject immutable objects
    private final UserService userService;

// @Autowired Because the current class has only one constructor, @Autowired here can be omitted
    public StudentController (UserService userService) {
        this.userService = userService;
    }

    public void sayHi() {
        System.out.println("student controller say hi");
        userService.sayHi();
    }
}

Run results:

Advantages

1. You can inject an immutable object (that is, an object modified with the final keyword)

  • The object modified by the final keyword must meet either of the following two conditions
  • Final modified objects need to be assigned directly
  • Final modified objects must be assigned values through the constructor

2. The injected object will not be modified

  • The constructor is only executed once as the class is loaded

3. Constructor injection can ensure that the object is completely initialized

  • When an object is created, the constructor of the class must be called to initialize the object
  • So when injecting through the constructor method, it will inevitably ensure that the object has been injected into the Bean object by the Spring container

4. Compared with attribute injection, constructor injection has better compatibility

  • Constructor injection works in any environment, whether IoC or non-IoC frameworks

@Resource

  • @Resource annotation is provided by JDK

The difference between @Autowired and @Resource

  • Different origins: @Autowired comes from Spring, while @Resource comes from JDK
  • @Autowired can be used for property injection, Settter injection, and constructor injection, while @Resource can only be used for property injection and Setter injection
  • The parameters set when using are different: Compared with @Autowired, @Resource supports more parameter settings, such as renaming

The difference between @Autowired and @Resource to find Bean objects

  • @Autowired will first search by type, then by name
  • And @Resource will first search based on the name, and then search based on the type

Example understanding

  • We create an entity class
//Ordinary user entity class
public class User {
    public Integer uid;
    public String username;
    public String password;
    public Integer age;

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}
  • Create another UserBean class and inject the User Bean object into the Spring container through this class
import com.java.demo.entity.User;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class UserBeans {
    @Bean
    public User getUserByName() {
        User user = new User();
        user.setUid(1);
        user.setUsername("Zhang San");
        user.setPassword("123456");
        user.setAge(18);
        return user;
    }

    @Bean(name = {"user1","u1"})
    public User getUserById() {
        User user = new User();
        user.setUid(1);
        user.setUsername("李思");
        user.setPassword("123456");
        user.setAge(18);
        return user;
    }
}
  • Inject User’s Bean object into the StudentController class through @Autowired
import com.java.demo.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

// Use @Controller annotation to store the current class in the spring container
@Controller
public class StudentController {

    // Use attribute injection
    @Autowired
    private User user;

    public void sayHi() {
        System.out.println("student controller say hi");
        user.getUsername();
    }
}

Run results:

Error report analysis:

  • Because we injected two Bean objects of the same User type into the Spring container in the UserBeans class
  • So when we use the @Autowired annotation to inject a Bean object, it will first search based on the type

  • At this time, two Bean objects of the User type were found in the Spring container
  • Thus, it will be searched according to the green box name. If there is a Bean object with the id of user in the Spring container, it will be injected directly

  • But the User type Bean object we store in the Spring container has an id of getUserByName and a second id of user1 or u1
  • So @Autowired cannot find the Bean object with the id of user, and an error occurs

Solution:

Option 1:

  • Modify the name of the green box and specify to inject a Bean object through id

Option 2:

  • Use the @Resource annotation and search by setting parameters

  • Of course the parameter name should be set to user1 to implement dependency injection
  • Using this method, you can rename the parameter name

Option 3:

  • Using in combination @Autowired and @Qualifier annotations

  • The @Qualifier annotation plays a filtering role, filtering out the Bean object with the id of user1 and injecting it
  • The effect of this method is the same as that of method 2
syntaxbug.com © 2021 All Rights Reserved.