IOC是一种思想
1.问题引出
原来我们要实现一个业务功能供用户调用的步骤
- UserDao接口设计 —— 对于数据库的操作的接口设计
package com.thhh.dao; public interface UserDao { public void getUser(); }
- UserImpl实现类 —— 对于数据库的操作的接口实现
package com.thhh.dao; public class UserDaoImpl implements UserDao{ public void getUser() { System.out.println("默认方式获取数据"); } }
- UserService业务接口设计 —— 对于业务实现的接口设计
package com.thhh.service; public interface UserService { public void getUser(); }
- UserServiceImpl实现类 —— 对于业务实现的接口实现(某个功能实现的逻辑代码就写在这里,主要还是调用Dao层,获取数据并处理之后返回给客户端)
package com.thhh.service; import com.thhh.dao.UserDao; import com.thhh.dao.UserDaoImpl; public class UserServiceImpl implements UserService{ UserDao userDao = new UserDaoImpl(); public void getUser() { userDao.getUser(); } }
- 客户端调用 / 测试 —— 客户端,用于调用业务层的接口,获取想要的数据返回给前端展示,这一层只需要知道业务层,而不需要 关心/知道 Dao层
import com.thhh.service.UserService; import com.thhh.service.UserServiceImpl; import org.junit.Test; public class MyTest { @Test public void test(){ UserService userService = new UserServiceImpl(); userService.getUser(); } }
假设程序猿对UserDao有了一种更好的实现,那么我们需要增加一个Userdao的实现类为UserDaoMySqlImpl
package com.thhh.dao;
public class UserDaoMySqlImpl implements UserDao{
public void getUser() {
System.out.println("MySql方式获取数据");
}
}
如果我们想要用户可以使用到新功能,我们就需要去修改UserServiceImpl中实例化的UserDao的实现类
测试
现在程序员又对功能实现进行了升级,那么我们需要增加一个Userdao的实现类为UserDaoOracleImpl
package com.thhh.dao;
public class UserDaoOracleImpl implements UserDao{
@Override
public void getUser() {
System.out.println("Oracle获取用户数据");
}
}
package com.thhh.service;
import com.thhh.dao.UserDao;
import com.thhh.dao.UserDaoImpl;
import com.thhh.dao.UserDaoMySqlImpl;
import com.thhh.dao.UserDaoOracleImpl;
public class UserServiceImpl implements UserService{
UserDao userDao = new UserDaoOracleImpl();
public void getUser() {
userDao.getUser();
}
}
测试
上面的例子中,只要我们新增了一种Dao实现,我们就需要手动的去修改service层的调用代码,这样显然是不合理的,如果service层的代码量庞大且很复杂,那么我们每次要修改的代码量将是及其庞大的
怎么解决这个问题呢?
在service层我们不去指定new哪一个Dao的实现类,而是直接留一个接口的引用,通过一个set方法将Dao接口引用的实例化交给客户端去做,由调用处/客户端 在要使用时使用set方法并且传入要调用的具体是哪一个Dao实现类的实例化对象,即可实例化原来service层要调用的Dao接口的引用
注意
上面的这个解决办法就是IOC(控制反转)的原型,这样的解决办法对程序运行的方式做出了根本性的变化
①原来决定service调用哪一个Dao实现类的是程序猿👨💻,为了让客户端能够调用功能,我们要在service层主动的去new一个Dao的对象,为客户选择怎么实现功能这个功能,用户只是被动的接收我们的选择。即程序主动选择,客户被动接受
②现在我们将new哪一个Dao接口的实现类的选择权力交给了客户端,即怎么实现相同功能的多种选择权力交给了用户,service层只是被动的接受servlet层传过来的Dao接口的实例化对象。即客户主动的选择,程序被动接受,这就是控制反转IOC
2.IOC定义
控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,所谓控制反转就是:获得依赖对象的方式反转了
采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection,DI)