Three commonly used injection methods in Spring

Spring implements IOC (inversion of control) through DI (dependency injection). There are three commonly used injection methods: constructor injection, setter injection, and annotation-based injection.

Constructor injection

Let's take a brief look at the structure of the test project, built with maven, four packages:
entity: storage entity, there is only one User class
dao: data access, one interface, two implementation classes
service: service layer, one interface, one implementation Class, the implementation class depends on IUserDao
test: The test package 
write picture description here 
registers UserService in spring's configuration file, and injects UserDaoJdbc into a parameterized constructor of UserService through the constructor-arg tag

<!-- 注册userService -->
<bean id="userService" class="com.lyu.spring.service.impl.UserService">
    <constructor-arg ref="userDaoJdbc"></constructor-arg>
</bean>
<!-- 注册jdbc实现的dao -->
<bean id="userDaoJdbc" class="com.lyu.spring.dao.impl.UserDaoJdbc"></bean>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

If there is only one constructor with parameters and the parameter type matches the type of the injected bean, it will be injected into that constructor.

public class UserService implements IUserService {

    private IUserDao userDao;

    public UserService(IUserDao userDao) {
        this.userDao = userDao;
    }

    public void loginUser() {
        userDao.loginUser();
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
@Test
public void testDI() {
    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    // 获取bean对象
    UserService userService = ac.getBean(UserService.class, "userService");
    // 模拟用户登录
    userService.loginUser();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Test print result: jdbc-login successful

Note: The loginUser method of simulating user login actually just prints an output statement. The output of the class implemented by jdbc is: jdbc-login is successful, and the output of the class implemented by mybatis is: mybatis-login is successful.  

Question 1: If there are multiple constructors with parameters and the parameter list of each constructor has properties to be injected, where will userDaoJdbc be injected?

public class UserService implements IUserService {

    private IUserDao userDao;
    private User user;

    public UserService(IUserDao userDao) {
        System.out.println("这是有一个参数的构造方法");
        this.userDao = userDao;
    }

    public UserService(IUserDao userDao, User user) {
        System.out.println("这是有两个参数的构造方法");
        this.userDao = userDao;
        this.user = user;
    }

    public void loginUser() {
        userDao.loginUser();
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21


Result: It will be injected into a constructor with only one parameter, and which constructor is injected after testing is independent of the order of the constructors

write picture description here

Question 2: If there is only one constructor, but there are two parameters, one is the parameter to be injected and the other is another type of parameter, can this injection be successful?

public class UserService implements IUserService {

    private IUserDao userDao;
    private User user;

    public UserService(IUserDao userDao, User user) {
        this.userDao = userDao;
        this.user = user;
    }

    public void loginUser() {
        userDao.loginUser();
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

Result: Failed, even if the parameter name userDao to be injected is specified through the name attribute in the cost-arg tag, it will fail.

write picture description here

问题三:如果我们想向有多个参数的构造方法中注入值该在配置文件中怎么写呢?

public class UserService implements IUserService {

    private IUserDao userDao;
    private User user;

    public UserService(IUserDao userDao, User user) {
        this.userDao = userDao;
        this.user = user;
    }

    public void loginUser() {
        userDao.loginUser();
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

参考写法:通过name属性指定要注入的值,与构造方法参数列表参数的顺序无关。

<!-- 注册userService -->
<bean id="userService" class="com.lyu.spring.service.impl.UserService">
    <constructor-arg name="userDao" ref="userDaoJdbc"></constructor-arg>
    <constructor-arg name="user" ref="user"></constructor-arg>
</bean>

<!-- 注册实体User类,用于测试 -->
<bean id="user" class="com.lyu.spring.entity.User"></bean>

<!-- 注册jdbc实现的dao -->
<bean id="userDaoJdbc" class="com.lyu.spring.dao.impl.UserDaoJdbc"></bean>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

问题四:如果有多个构造方法,每个构造方法只有参数的顺序不同,那通过构造方法注入多个参数会注入到哪一个呢?

public class UserService implements IUserService {

    private IUserDao userDao;
    private User user;

    public UserService(IUserDao userDao, User user) {
        System.out.println("这是第二个构造方法");
        this.userDao = userDao;
        this.user = user;
    }

    public UserService(User user, IUserDao userDao) {
        System.out.println("这是第一个构造方法");
        this.userDao = userDao;
        this.user = user;
    }

    public void loginUser() {
        userDao.loginUser();
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

结果:哪个构造方法在前就注入哪一个,这种情况下就与构造方法顺序有关。

write picture description here

setter注入

配置文件如下:

<!-- 注册userService -->
<bean id="userService" class="com.lyu.spring.service.impl.UserService">
    <!-- 写法一 -->
    <!-- <property name="UserDao" ref="userDaoMyBatis"></property> -->
    <!-- 写法二 -->
    <property name="userDao" ref="userDaoMyBatis"></property>
</bean>

<!-- 注册mybatis实现的dao -->
<bean id="userDaoMyBatis" class="com.lyu.spring.dao.impl.UserDaoMyBatis"></bean>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

注:上面这两种写法都可以,spring会将name值的每个单词首字母转换成大写,然后再在前面拼接上”set”构成一个方法名,然后去对应的类中查找该方法,通过反射调用,实现注入。

切记:name属性值与类中的成员变量名以及set方法的参数名都无关,只与对应的set方法名有关,下面的这种写法是可以运行成功的

public class UserService implements IUserService {

    private IUserDao userDao1;

    public void setUserDao(IUserDao userDao1) {
        this.userDao1 = userDao1;
    }

    public void loginUser() {
        userDao1.loginUser();
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

还有一点需要注意:如果通过set方法注入属性,那么spring会通过默认的空参构造方法来实例化对象,所以如果在类中写了一个带有参数的构造方法,一定要把空参数的构造方法写上,否则spring没有办法实例化对象,导致报错。 
write picture description here

基于注解的注入

在介绍注解注入的方式前,先简单了解bean的一个属性autowire,autowire主要有三个属性值:constructor,byName,byType。

  • constructor:通过构造方法进行自动注入,spring会匹配与构造方法参数类型一致的bean进行注入,如果有一个多参数的构造方法,一个只有一个参数的构造方法,在容器中查找到多个匹配多参数构造方法的bean,那么spring会优先将bean注入到多参数的构造方法中。

  • byName:被注入bean的id名必须与set方法后半截匹配,并且id名称的第一个单词首字母必须小写,这一点与手动set注入有点不同。

  • byType: Finds all set methods and injects beans that conform to the parameter type.


Let’s go to the main topic: register beans by annotation, and inject dependencies. 

There are four main types of annotations that can register beans. Each annotation can be used arbitrarily, but the semantics are different:

  1. @Component: can be used to register all beans
  2. @Repository: mainly used to register beans of the dao layer
  3. @Controller: mainly used to register beans of the control layer
  4. @Service: Mainly used to register beans of the service layer

There are two main types of description dependencies:

  • @Resource: An annotation of java, the default is byName to match the id of the bean with the same property name. If it is not found, it will be searched by byType. If more than one byType is found, use @Qualifier annotation (spring annotation) Specifies a bean with a specific name.
@Resource
@Qualifier("userDaoMyBatis")
private IUserDao userDao;

public UserService(){

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • @Autowired: spring annotation, the default is to use byName to match the id of the bean with the same property name. If it is not found, it is searched by byType. If more than one is found, use @Qualifier annotation to define which one to use.
@Autowired
@Qualifier("userDaoJdbc")
private IUserDao userDao;
  • 1
  • 2
  • 3

Written at the end: Although there are so many injection methods, in fact, the classes written by oneself generally use annotations to register classes, and use @Autowired to describe dependencies for injection. Generally, there is only one implementation class (jdbc or hibernate or mybatis), unless there are major changes in the project, so the @Qualifier tag is used less; but when using the API of other components, the xml configuration file is used to register classes and describe dependencies, because you can't change other people's source code Well.

Reprinted in: https://blog.csdn.net/a909301740/article/details/78379720

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325771474&siteId=291194637