Spring经常使用的三种注入方式

Spring经过DI(依赖注入)实现IOC(控制反转),经常使用的注入方式主要有三种:构造方法注入,setter注入,基于注解的注入。java

构造方法注入

先简单看一下测试项目的结构,用maven构建的,四个包:
entity:存储实体,里面只有一个User类
dao:数据访问,一个接口,两个实现类
service:服务层,一个接口,一个实现类,实现类依赖于IUserDao
test:测试包 
这里写图片描述 
在spring的配置文件中注册UserService,将UserDaoJdbc经过constructor-arg标签注入到UserService的某个有参数的构造方法spring

<!-- 注册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>

若是只有一个有参数的构造方法而且参数类型与注入的bean的类型匹配,那就会注入到该构造方法中。mybatis

public class UserService implements IUserService { private IUserDao userDao; public UserService(IUserDao userDao) { this.userDao = userDao; } public void loginUser() { userDao.loginUser(); } }
@Test public void testDI() { ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); // 获取bean对象 UserService userService = ac.getBean(UserService.class, "userService"); // 模拟用户登陆 userService.loginUser(); }

测试打印结果:jdbc-登陆成功

注:模拟用户登陆的loginUser方法其实只是打印了一条输出语句,jdbc实现的类输出的是:jdbc-登陆成功,mybatis实现的类输出的是:mybatis-登陆成功。 

问题一:若是有多个有参数的构造方法而且每一个构造方法的参数列表里面都有要注入的属性,那userDaoJdbc会注入到哪里呢?app

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(); } }


结果:会注入到只有一个参数的构造方法中,而且通过测试注入哪个构造方法与构造方法的顺序无关

这里写图片描述maven

问题二:若是只有一个构造方法,可是有两个参数,一个是待注入的参数,另外一个是其余类型的参数,那么此次注入能够成功吗?测试

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(); } }

结果:失败了,即便在costract-arg标签里面经过name属性指定要注入的参数名userDao也会失败.

这里写图片描述this

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

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(); } }

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

<!-- 注册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>

问题四:若是有多个构造方法,每一个构造方法只有参数的顺序不一样,那经过构造方法注入多个参数会注入到哪个呢?code

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(); } }

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

这里写图片描述

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>

注:上面这两种写法均可以,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(); } }

还有一点须要注意:若是经过set方法注入属性,那么spring会经过默认的空参构造方法来实例化对象,因此若是在类中写了一个带有参数的构造方法,必定要把空参数的构造方法写上,不然spring没有办法实例化对象,致使报错。 
这里写图片描述

基于注解的注入

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

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

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

  • byType:查找全部的set方法,将符合符合参数类型的bean注入。


下面进入正题:注解方式注册bean,注入依赖 

主要有四种注解能够注册bean,每种注解能够任意使用,只是语义上有所差别:

  1. @Component:能够用于注册全部bean
  2. @Repository:主要用于注册dao层的bean
  3. @Controller:主要用于注册控制层的bean
  4. @Service:主要用于注册服务层的bean

描述依赖关系主要有两种:

  • @Resource:java的注解,默认以byName的方式去匹配与属性名相同的bean的id,若是没有找到就会以byType的方式查找,若是byType查找到多个的话,使用@Qualifier注解(spring注解)指定某个具体名称的bean。
@Resource @Qualifier("userDaoMyBatis") private IUserDao userDao; public UserService(){
  • @Autowired:spring注解,默认是以byType的方式去匹配与属性名相同的bean的id,若是没有找到,就经过byName的方式去查找,
@Autowired @Qualifier("userDaoJdbc") private IUserDao userDao;

写在最后:虽然有这么多的注入方式,可是实际上开发的时候本身编写的类通常用注解的方式注册类,用@Autowired描述依赖进行注入,通常实现类也只有一种(jdbc or hibernate or mybatis),除非项目有大的变更,因此@Qualifier标签用的也较少;可是在使用其余组件的API的时候用的是经过xml配置文件来注册类,描述依赖,由于你不能去改人家源码嘛。