1. 依赖注入的概念
当某个java对象(调用者)调用另个一java对象(被调用者,即被依赖对象)时,在传统的模式下,调用者内部通常会采用“new 被调用者”的方式来创建被调用者的实例对象,然后由该实例对象调用它的方法。
这种方式会导致调用者与被调用者之间的耦合性增加,不利于项目的维护与升级。而Spring框架完美的解决了这个问题,对象的实例不在由调用者来创建,而是由Spring容器来创建,Spring容器会负责控制程序之间的关系,而不再是由调用者的程序代码控制,这样控制权由应用代码转移到了Spring容器,反生了控制反转,这就是Spring里面的控制反转(IoC)。
由Spring容器负责将被调用者的对象赋值给调用者的成员变量,这一行为就被称为依赖注入
文字枯燥难以理解,下面结合一个实例来理解上面的含义:
2. 传统模式的依赖关系
创建UserDao,UserDao内部有一个save()方法
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("UserDaoImpl of save running");
}
}
创建 UserService,UserService 的save()方法内部在调用 UserDao的save() 方法
public class UserServiceImpl implements UserService {
@Override
public void save() {
UserDaoImpl userDao=new UserDaoImpl();
userDao.save();
}
}
将 UserServiceImpl 的创建权交给 Spring
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl"/>
从 Spring 容器中获得 UserService 进行操作
@org.junit.Test
public void test(){
//获取Spring容器
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean对象
UserService userService = (UserService) applicationContext.getBean("userService");
//bean对象调用方法
userService.save();
}
可见,传统模式下调用者userservice和被调用者userDao之间依赖关系,由调用者userService内部的应用程序代码控制,,这就导致了调用者和被调用者的都合度增加。利用Spring的依赖注入就可以解决掉这个弊端。
3. 依赖注入的实现式
3.1setter方式的依赖注入实现
setter方式注入:指Spring容器使用setter注入被依赖的实例。通过调用无参构造器或者无参静态工厂实例化bean后调用set方法实现依赖的注入
需求:使用依赖注入的方式实现在UserService的save方法里面调用userDao的save方法
创建UserDao,UserDao内部有一个save()方法
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("UserDaoImpl of save running");
}
}
创建 UserService,UserService 的save()方法内部在调用 UserDao的save() 方法
public class UserServiceImpl implements UserService {
//声明userDao属性
private UserDaoImpl userDao;
//添加userDao属性的set方法,用于实现依赖注入
public void setUserDao(UserDaoImpl userDao) {
this.userDao = userDao;
}
@Override
public void save() {
//调用userDao的方法
userDao.save();
}
}
将对象的创建权交给spring容器,并有Spring容器实现依赖注入
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="userService" class="com.itheima.dao.impl.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
从 Spring 容器中获得 UserService 进行操作
@org.junit.Test
public void test(){
//获取Spring容器
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean对象
UserService userService = (UserService) applicationContext.getBean("userService");
//bean对象调用方法
userService.save();
}
set方式的命名空间引入
P命名空间注入本质也是set方法注入,但比起上述的set方法注入更加方便,主要体现在配置文件中,如下:
首先,需要引入P命名空间:
xmlns:p="http://www.springframework.org/schema/p"
其次,需要修改注入方式
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl" p:userDaoref="userDao"/
3.2有参构造函数实现依赖注入
创建UserDao,创建save()方法
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("UserDaoImpl of save running");
}
}
创建UserService,并创建userDao属性和save()方法
public class UserServiceImpl implements UserService {
// 有参构造方法注入参数
private UserDaoImpl userDao;
public UserServiceImpl(UserDaoImpl userDao) {
this.userDao = userDao;
System.out.println("UserService的有参构造函数被调用了");
}
@Override
public void save() {
//调用userDao的方法
userDao.save();
}
}
从 Spring 容器中获得 UserService 进行操作
@org.junit.Test
public void test(){
//获取Spring容器
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean对象
UserService userService = (UserService) applicationContext.getBean("userService");
//bean对象调用方法
userService.save();
}
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="userService" class="com.itheima.dao.impl.UserServiceImpl">
<constructor-arg name="userDao" ref="userDao"/>
</bean>
看结果:
还有一点是使用有参构造来注入依赖的时候,bean实例是通过有参构造函数来创建的。