一、基本思想
控制反转IoC(Inversion of Control),是一种设计思想,而依赖注入(Dependency Injection,DI)是实现IoC的一种方法,也有人认为DI仅仅是IoC的另一种说法。IoC思想是为了降低程序代码之间的耦合度。
假设我们使用三层架构实现一个用户登录的功能,包括了下面这些类:
pojo包
User实体类(定义了用户的姓名、密码等字段)
dao包
UserDao接口(定义了一些基本的数据库增删改查操作)
UserDaoImpl实现类(实现了UserDao接口的方法)
service包
UserService接口(定义了一些与用户有关的操作,如登录,注册等,这些操作要用到基本的数据库增删改查操作)
UserServiceImpl实现类(实现了UserService接口的方法)
test包
Test类(由于测试UserServiceImpl中的方法)
我们在编写UserServiceImpl实现类的时候,需要用到UserDao接口的相关数据库方法。如果按照一般的写法,这里我们会创建一个UserDaoImpl对象,然后去使用这个对象中的方法。即下面的代码:
private UserDao userDao = new UserDaoImpl();
... ...
@Override
public void login() {
... ...
userDao.find();
}
@Override
public void register() {
... ...
userDao.insert();
userDao.find();
}
这样写的话,代码耦合度高,存在一些问题:
如果有不同的userDao 实现类,比如使用了MySql数据库的,使用了SQLite数据库的。这样如果要去修改的话,我们需要不断的修改源代码中的内容。
由于UserServiceImpl依赖于UserDaoImpl,如果两个类是由不同的团队开发的,则UserServiceImpl团队不能先工作,因为在UserDaoImpl完成之前,UserServiceImpl是无法通过编译。
为了解决耦合度高问题,我们使用依赖注入的思想:
在编写UserServiceImpl实现类的时候,我们不再是new一个UserDaoImpl对象,而是使用构造器/setter方法,通过参数的方式导入(注入)这个对象。
这样,本来是UserServiceImpl应用内部应该创建及维护的工作,被转义到调用这个类的外部类。对象创建的控制权就由应用内转移到了外部容器,控制权的转移就是所谓的反转,这就是控制反转的基本思想。
public class UserServiceImpl implements UserService {
private UserDao userDao;
// 通过 构造方法 注入 userDao
public UserServiceImpl(UserDao userDao){
this.userDao = userDao;
}
// 通过 setter方法 注入 userDao
public void setUserDao(UserDao userDao){
this.userDao = userDao;
}
... ...
}
public class AppTest {
... ...
@Test
public void test(){
UserDao userDao = new UserDaoImpl();
// userDao对象创建的控制权由UserServiceImpl类转移到AppTest测试
UserService userService = new UserServiceImpl(userDao);
userService.login();
userService.register();
}
... ...
}
二、Spring中的使用
在Spring中,我们把创建UserServiceImpl对象、UserServiceImpl导入UserDao的工作交给IoC容器去做,即下面两段代码:
UserDao userDao = new UserDaoImpl();
UserService userService = new UserServiceImpl(userDao);
我们只需要在xml配置文件里面写好UserService类的定义,UserService类需要注入的UserDao类(即类和类之间的关系依赖)。这样当我们需要修改userDao对象时,就只需要在xml配置文件中去修改,而不需要改动UserServiceImpl中的代码。
... ...
<!--IoC容器创建UserDaoImpl对象-->
<bean id="userDao" class="com.shj.dao.impl.UserDaoImpl"/>
<!--IoC容器创建UserServiceImpl对象-->
<bean id="userService" class="com.shj.service.impl.UserServiceImpl">
<!--调用UserServiceImpl中的setUserDao方法-->
<property name="userDao" ref="userDao"/>
</bean>
... ...
IoC容器自动地帮助我们创建好类,并注入相关的信息。我们在测试类中去调用IoC容器提供的ApplicationContext接口,通过接口中的getBean()方法就可以获取到我们需要的类并使用。
@Test
public void testSetterDI(){
// 读取app.xml配置文件,获取ApplicationContext接口
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("app.xml");
// 通过ApplicationContext接口获取UserService对象
UserService userService = (UserService) applicationContext.getBean("userService");
// 使用
userService.login();
userService.register();
}
下面是整体的流程图:
附:演示的所有的代码
pojo包
User实体类(定义了用户的姓名、密码等字段)
由于没有使用数据库,后面UserDao的方法也只是演示,故没有写User类。
dao包
UserDao接口(定义了一些基本的数据库增删改查操作)
package com.app.dao;
public interface UserDao {
void insert();
void find();
}
UserDaoImpl实现类(实现了UserDao接口的方法)
package com.app.dao.impl;
import com.app.dao.UserDao;
public class UserDaoImpl implements UserDao {
@Override
public void insert() {
System.out.println("dao类.insert");
}
@Override
public void find() {
System.out.println("dao类.find");
}
}
service包
UserService接口(定义了一些与用户有关的操作,如登录,注册等,这些操作要用到基本的数据库增删改查操作)
package com.app.service;
public interface UserService {
void login();
void register();
}
UserServiceImpl实现类(实现了UserService接口的方法)
package com.app.service.impl;
import com.app.dao.UserDao;
import com.app.service.UserService;
public class UserServiceImpl implements UserService {
private UserDao userDao;
// 通过 构造方法 注入 userDao
// public UserServiceImpl(UserDao userDao){
// this.userDao = userDao;
// }
// 通过 setter方法 注入 userDao
public void setUserDao(UserDao userDao){
this.userDao = userDao;
}
@Override
public void login() {
System.out.println("服务实现类.login");
userDao.find();
}
@Override
public void register() {
System.out.println("服务实现类.register");
userDao.insert();
userDao.find();
}
}
test包
Test类(由于测试UserServiceImpl中的方法)
package com.app.test;
import com.app.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
@Test
public void test(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("app.xml");
UserService userService = (UserService) applicationContext.getBean("userService");
userService.login();
userService.register();
}
}
配置文件 app.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--1.构造器注入-->
<!-- <bean id="userDao" class="com.app.dao.impl.UserDaoImpl"/>-->
<!-- <bean id="userService" class="com.app.service.impl.UserServiceImpl">-->
<!-- <constructor-arg name="userDao" ref="userDao"/>-->
<!-- </bean>-->
<!--2.setter方法注入-->
<bean id="userDao" class="com.app.dao.impl.UserDaoImpl"/>
<bean id="userService" class="com.app.service.impl.UserServiceImpl">
<!--调用UserServiceImpl中的setUserDao方法-->
<property name="userDao" ref="userDao"/>
</bean>
</beans>