​ 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.    
  5.    // bean的文件路径
  6.    private String classPath;

  7. public String getBeanId() {
  8. return beanId;
  9. }
  10. public void setBeanId(String beanId) {
  11. this.beanId = beanId;
  12. }
  13. public String getClassPath() {
  14. return classPath;
  15. }
  16. public void setClassPath(String classPath) {
  17. this.classPath = classPath;
  18. }
  19. }

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. }

测试结果截图:

从代码里面可以看出来,我们是没有直接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.    
  9.    // ....省略set和get方法
  10. }

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. }

测试结果截图:

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

什么是控制反转(IoC)?什么是依赖注入(DI)?以及实现原理的更多相关文章

  1. 关于.NET中的控制反转(三)- 依赖注入之 Autofac

    一.Autofac简介 Autofac和其他容器的不同之处是它和C#语言的结合非常紧密,在使用过程中对你的应用的侵入性几乎为零,更容易与第三方的组件集成.Autofac的主要特性如下: 组件侵入性为零 ...

  2. Spring 什么是 IOC 控制反转 ?什么是依赖注入?spring的用处 好处 为什么要用

    韩梦飞沙  韩亚飞  313134555@qq.com  yue31313  han_meng_fei_sha Spring是一个开源的控制反转(Inversion of Control ,IoC)和 ...

  3. ASP.NET中IOC容器Autofac(依赖注入DI 控制反转IOC)

    IOC的一个重点是在程序运行中,动态的向某个对象提供它所需要的其他对象.这一点是通过DI来实现的.Autofac则是比较流行的一款IOC容器. IoC和DI有什么关系呢?其实它们是同一个概念的不同角度 ...

  4. 关于.NET中的控制反转(二)- 依赖注入之 MEF

    一.MEF是什么 Managed Extensibility Framework (MEF) 是用于创建可扩展的轻量级应用程序的库. 它让应用程序开发人员得以发现和使用扩展且无需配置. 它还让扩展开发 ...

  5. 控制反转IOC与依赖注入DI - 理论篇

    学无止境,精益求精 十年河东十年河西,莫欺少年穷 昨天是五一小长假归来上班的第一天,身体疲劳,毫无工作热情.于是就看看新闻,喝喝茶,荒废了一天 也就在昨天,康美同事张晶童鞋让我学习下IOC的理论及实现 ...

  6. ASP.NET MVC 中 Autofac依赖注入DI 控制反转IOC 了解一下

    先简单了解一这个几个 名词的意思. 控制反转(IOC) 依赖注入(DI) 并不是某种技术. 而是一种思想.一种面向对象编程法则 什么是控制反转(IOC)?  什么是依赖注入(DI) 可以点击下面链接 ...

  7. ADO.NET .net core2.0添加json文件并转化成类注入控制器使用 简单了解 iTextSharp实现HTML to PDF ASP.NET MVC 中 Autofac依赖注入DI 控制反转IOC 了解一下 C# AutoMapper 了解一下

    ADO.NET   一.ADO.NET概要 ADO.NET是.NET框架中的重要组件,主要用于完成C#应用程序访问数据库 二.ADO.NET的组成 ①System.Data  → DataTable, ...

  8. 依赖注入 DI 控制反转 IOC MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  9. 控制反转(IoC)与依赖注入(DI)

    前言 最近在学习Spring框架,它的核心就是IoC容器.要掌握Spring框架,就必须要理解控制反转的思想以及依赖注入的实现方式.下面,我们将围绕下面几个问题来探讨控制反转与依赖注入的关系以及在Sp ...

  10. 依赖注入 DI 控制反转 IOC 概念 案例 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

随机推荐

  1. laravel向视图传递变量

    向视图中传递变量 我们在开发web应用当中,通常都不是为了写静态页面而生的,我们需要跟数据打交道,那么这个时候,问题就来了,在一个MVC的框架中,怎么将数据传给视图呢?比如我们要在 ArticleCo ...

  2. cc30a_demo-CppPrimer_友元与继承-txwtech友元关系不能继承-要明确授予友元

    //友元可以访问类的private与protected成员//友元关系不能继承-要明确授予友元 #include <iostream>//CppPrimer_友元与继承-txwtech-- ...

  3. 国外的教授都说,用这个方式21天就能学会python,这是中国速度

    你尝试过吗?按照这个方式,用21天就能学会python编程.     在今年的疫情期间,在家的时间何止21天,有这样一位做财务的朋友,为了提高自己的数据分析能力,在家通过这个方式,跟着21天的规划,坚 ...

  4. Linux常用命令之文件磁盘管理

    前言 本文知识点是曾经学习过程中收录整理的,方便学习使用. 一>Linux常用基本命令 Linux命令格式:command [-options] [parameter1] ... command ...

  5. 多语言工作者の十日冲刺<8/10>

    这个作业属于哪个课程 软件工程 (福州大学至诚学院 - 计算机工程系) 这个作业要求在哪里 团队作业第五次--Alpha冲刺 这个作业的目标 团队进行Alpha冲刺--第八天(05.07) 作业正文 ...

  6. java创建图片的缩略图

    //java创建图片的缩略图private void createThumbnail(String filename, int thumbWidth, int thumbHeight, int qua ...

  7. disruptor架构四 多生产者多消费者执行

    1.首先介绍下那个时候使用RingBuffer,那个时候使用disruptor ringBuffer比较适合场景比较简单的业务,disruptor比较适合场景较为复杂的业务,很多复杂的结果必须使用di ...

  8. 02.Scrapy-Demo

    Scrapy入门实战 采集目标:采集西祠网的IP代理 包括 IP PORT 1. 新建项目 scrapy startproject xicidailiSpider # scrapy 新建项目 项目名 ...

  9. Python函数参数详解

    Python函数参数详解 形参与实参 什么是形参 在定义函数阶段定义的参数称之为形式参数,简称形参,相当于变量名. 什么是实参 在调用函数阶段传入的值称为实际参数,简称实参.相当于"变量值& ...

  10. 博弈论Nim取子问题,困扰千年的问题一行代码解决

    本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是算法与数据结构专题26篇文章,我们来看看一个新的博弈论模型--Nim取子问题. 这个博弈问题非常古老,延续长度千年之久,一直到20世纪 ...