Spring通过DI(依赖注入)实现IOC(控制反转),常用的注入方式主要有三种:构造方法注入,setter注入,基于注解的注入。
先简单看一下测试项目的结构,用maven构建的,四个包:
entity:存储实体,里面只有一个User类
dao:数据访问,一个接口,两个实现类
service:服务层,一个接口,一个实现类,实现类依赖于IUserDao
test:测试包
在spring的配置文件中注册UserService,将UserDaoJdbc通过constructor-arg标签注入到UserService的某个有参数的构造方法
如果只有一个有参数的构造方法并且参数类型与注入的bean的类型匹配,那就会注入到该构造方法中。
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会注入到哪里呢?
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();
}
}
结果:会注入到只有一个参数的构造方法中,并且经过测试注入哪一个构造方法与构造方法的顺序无关
问题二:如果只有一个构造方法,但是有两个参数,一个是待注入的参数,另一个是其他类型的参数,那么这次注入可以成功吗?
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也会失败.
问题三:如果我们想向有多个参数的构造方法中注入值该在配置文件中怎么写呢?
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属性指定要注入的值,与构造方法参数列表参数的顺序无关。
问题四:如果有多个构造方法,每个构造方法只有参数的顺序不同,那通过构造方法注入多个参数会注入到哪一个呢?
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();
}
}
结果:哪个构造方法在前就注入哪一个,这种情况下就与构造方法顺序有关。
配置文件如下:
注:上面这两种写法都可以,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。
下面进入正题:注解方式注册bean,注入依赖
主要有四种注解可以注册bean,每种注解可以任意使用,只是语义上有所差异:
描述依赖关系主要有两种:
@Resource
@Qualifier("userDaoMyBatis")
private IUserDao userDao;
public UserService(){
@Autowired
@Qualifier("userDaoJdbc")
private IUserDao userDao;
写在最后:虽然有这么多的注入方式,但是实际上开发的时候自己编写的类一般用注解的方式注册类,用@Autowired描述依赖进行注入,一般实现类也只有一种(jdbc or hibernate or mybatis),除非项目有大的变动,所以@Qualifier标签用的也较少;但是在使用其他组件的API的时候用的是通过xml配置文件来注册类,描述依赖,因为你不能去改人家源码嘛。