什么是控制反转(IoC)?什么是依赖注入(DI)?以及实现原理

分手后的思念是犯贱 2022-05-10 02:08 386阅读 0赞

​ IoC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活

IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找

​ DI—Dependency Injection,即“依赖注入”:是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:

●谁依赖于谁:当然是应用程序依赖于IoC容器;

●为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;

●谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;

●注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)

本质上IoC和DI是同一思想下不同维度的表现 ,用通俗的话说就是,IoC是bean的注册,DI是bean的初始化

源码实现原理:

参考文章:

https://blog.csdn.net/lisongjia123/article/details/52129340

https://blog.csdn.net/lisongjia123/article/details/52134396

上面的文章根据源码进行分析,过程比较复杂,因此我进行了一个高度的抽象,希望来描述它们的实现原理:

简单的IoC控制反转代码实现,定义测试的Bean,Student和Teacher

  1. public class Teacher {
  2. private String tName;
  3. // ....get set方法省略
  4. }
  5. public class Student {
  6. private String name;
  7. private String age;
  8. private Teacher teacher;
  9. // ....get set方法省略
  10. }

BeanDefined类,对bean的描述类:

  1. public class BeanDefined {
  2. // bean的id
  3. private String beanId;
  4. // bean的文件路径
  5. private String classPath;
  6. public String getBeanId() {
  7. return beanId;
  8. }
  9. public void setBeanId(String beanId) {
  10. this.beanId = beanId;
  11. }
  12. public String getClassPath() {
  13. return classPath;
  14. }
  15. public void setClassPath(String classPath) {
  16. this.classPath = classPath;
  17. }
  18. }

BeanFactory —- 生成Bean的容器类:

  1. public class BeanFactory {
  2. // 存放bean的集合
  3. private List<BeanDefined> beanDefinedList;
  4. public List<BeanDefined> getBeanDefinedList() {
  5. return beanDefinedList;
  6. }
  7. public void setBeanDefinedList(List<BeanDefined> beanDefinedList) {
  8. this.beanDefinedList = beanDefinedList;
  9. }
  10. /**
  11. * 获取bean实例
  12. *
  13. * @param beanId
  14. * @return
  15. * @throws Exception
  16. */
  17. public Object getBean(String beanId) throws Exception {
  18. Object instance;
  19. for (BeanDefined bean : beanDefinedList) {
  20. if (beanId.equals(bean.getBeanId())) {
  21. String classPath = bean.getClassPath();
  22. Class classFile = Class.forName(classPath);
  23. // 在spring中调用默认的构造方法,这里我们也调用默认的构造方法
  24. instance = classFile.newInstance();
  25. return instance;
  26. }
  27. }
  28. return null;
  29. }
  30. }

测试类:

  1. public class Test {
  2. public static void main(String[] args) throws Exception {
  3. // 1、声明注册bean
  4. BeanDefined beanObj = new BeanDefined();
  5. beanObj.setBeanId("student");
  6. beanObj.setClassPath("com.pojo.Student");
  7. List<BeanDefined> beanList = new ArrayList<BeanDefined>();
  8. beanList.add(beanObj);
  9. // 2、声明一个BeanFactory,类似于Spring中的ApplicationContext
  10. BeanFactory factory = new BeanFactory();
  11. factory.setBeanDefinedList(beanList);
  12. // 3、开发人员向BeanFactory索要实例对象
  13. Student student = (Student) factory.getBean("student");
  14. System.out.println(student);
  15. }
  16. }

测试结果截图:

70

从代码里面可以看出来,我们是没有直接new学生类的,主要的思想是,定义BeanDefined对象添加进集合中,通过BeanFactory为我们生产出需要的对象,其中用到的核心技术就是:反射

用代码实现简单的依赖注入:

BeanDefined类需要做一定的修改:

  1. public class BeanDefined {
  2. // bean的id
  3. private String beanId;
  4. // bean的文件路径
  5. private String classPath;
  6. // 存放属性的集合
  7. private Map<String, String> propertyMap = new HashMap<>();
  8. // ....省略set和get方法
  9. }

BeanFactory类需要做如下的修改:

  1. public class BeanFactory {
  2. // 存放bean的集合
  3. private List<BeanDefined> beanDefinedList;
  4. public List<BeanDefined> getBeanDefinedList() {
  5. return beanDefinedList;
  6. }
  7. public void setBeanDefinedList(List<BeanDefined> beanDefinedList) {
  8. this.beanDefinedList = beanDefinedList;
  9. }
  10. /**
  11. * 获取bean实例
  12. *
  13. * @param beanId
  14. * @return
  15. * @throws Exception
  16. */
  17. public Object getBean(String beanId) throws Exception {
  18. Object instance;
  19. for (BeanDefined bean : beanDefinedList) {
  20. if (beanId.equals(bean.getBeanId())) {
  21. String classPath = bean.getClassPath();
  22. Class classFile = Class.forName(classPath);
  23. // 在spring中调用默认的构造方法,这里我们也调用默认的构造方法
  24. instance = classFile.newInstance();
  25. // 获取bean的属性配置
  26. Map<String, String> propertyMap = bean.getPropertyMap();
  27. if(propertyMap !=null) {
  28. setValue(instance, classFile, propertyMap);
  29. }
  30. return instance;
  31. }
  32. }
  33. return null;
  34. }
  35. /**
  36. * 依赖注入的方法
  37. *
  38. * @param instance 当前的实例对象
  39. * @param classFile 当前实例对象所关联的类文件
  40. * @param propertyMap 属性
  41. */
  42. public void setValue(Object instance, Class classFile, Map<String, String> propertyMap) throws Exception {
  43. if(propertyMap !=null ) {
  44. /***
  45. * 获取map的所有属性配置
  46. */
  47. Set<String> proper = propertyMap.keySet();
  48. for(String string : proper) {
  49. // 通过字符串拼接,拼出set方法名
  50. char c = string.toUpperCase().charAt(0);
  51. String s = "set" + c + string.substring(1);
  52. // 获取当前属性的类型
  53. Field field = classFile.getDeclaredField(string);
  54. // 根据属性的类型进行调用
  55. Method m = instance.getClass().getMethod(s, field.getType());
  56. /**
  57. * 直接try注入普通类型,或者catch注入bean工厂中的其他类型
  58. */
  59. try {
  60. m.invoke(instance, propertyMap.get(string));
  61. } catch (Exception e) {
  62. m.invoke(instance, getBean(propertyMap.get(string)));
  63. }
  64. }
  65. }
  66. }
  67. }

测试类代码需要做如下修改:

  1. public class Test {
  2. public static void main(String[] args) throws Exception {
  3. // 1、声明注册bean
  4. BeanDefined beanObj = new BeanDefined();
  5. beanObj.setBeanId("student");
  6. beanObj.setClassPath("com.pojo.Student");
  7. // 设置 property
  8. Map<String, String> propertyMap = beanObj.getPropertyMap();
  9. propertyMap.put("name", "kxm");
  10. propertyMap.put("age", "22岁");
  11. propertyMap.put("teacher", "teacher");
  12. // 注册教师类
  13. BeanDefined teacher = new BeanDefined();
  14. teacher.setBeanId("teacher");
  15. teacher.setClassPath("com.pojo.Teacher");
  16. List<BeanDefined> beanList = new ArrayList<BeanDefined>();
  17. beanList.add(beanObj);
  18. beanList.add(teacher);
  19. // 2、声明一个BeanFactory,类似于Spring中的ApplicationContext
  20. BeanFactory factory = new BeanFactory();
  21. factory.setBeanDefinedList(beanList);
  22. // 3、开发人员向BeanFactory索要实例对象
  23. Student student = (Student) factory.getBean("student");
  24. System.out.println(student);
  25. }
  26. }

测试结果截图:

70 1

仔细分析代码,我们可以发现,没有显示的new对象,也没用用set方法去赋值,但是模拟出来了依赖注入的效果,这也是Spring中依赖注入的原理,当然这里是最简单的实现,剩下的路,还需要骚年你自己走哇~

发表评论

表情:
评论列表 (有 0 条评论,386人围观)

还没有评论,来说两句吧...

相关阅读