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(); } }
结果:哪一个构造方法在前就注入哪个,这种状况下就与构造方法顺序有关。
配置文件以下:
<!-- 注册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。
下面进入正题:注解方式注册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配置文件来注册类,描述依赖,由于你不能去改人家源码嘛。