什么是控制反转(IoC)?什么是依赖注入(DI)?以及实现原理
 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
public class Teacher {
    private String tName;
    // ....get set方法省略
}
public class Student {
    private String name;
    private String age;
    private Teacher teacher;
    // ....get set方法省略
}BeanDefined类,对bean的描述类:
public class BeanDefined {
    // bean的id
    private String beanId;
    
    // bean的文件路径
    private String classPath;
    public String getBeanId() {
        return beanId;
    }
    public void setBeanId(String beanId) {
        this.beanId = beanId;
    }
    public String getClassPath() {
        return classPath;
    }
    public void setClassPath(String classPath) {
        this.classPath = classPath;
    }
}BeanFactory --- 生成Bean的容器类:
public class BeanFactory {
    // 存放bean的集合
    private List<BeanDefined> beanDefinedList;
    public List<BeanDefined> getBeanDefinedList() {
        return beanDefinedList;
    }
    public void setBeanDefinedList(List<BeanDefined> beanDefinedList) {
        this.beanDefinedList = beanDefinedList;
    }
    /**
     * 获取bean实例
     *
     * @param beanId
     * @return
     * @throws Exception
     */
    public Object getBean(String beanId) throws Exception {
        Object instance;
        for (BeanDefined bean : beanDefinedList) {
            if (beanId.equals(bean.getBeanId())) {
                String classPath = bean.getClassPath();
                Class classFile = Class.forName(classPath);
                // 在spring中调用默认的构造方法,这里我们也调用默认的构造方法
                instance = classFile.newInstance();
                return instance;
            }
        }
        return null;
    }
}测试类:
public class Test {
    public static void main(String[] args) throws Exception {
        // 1、声明注册bean
        BeanDefined beanObj = new BeanDefined();
        beanObj.setBeanId("student");
        beanObj.setClassPath("com.pojo.Student");
        List<BeanDefined> beanList = new ArrayList<BeanDefined>();
        beanList.add(beanObj);
        // 2、声明一个BeanFactory,类似于Spring中的ApplicationContext
        BeanFactory factory = new BeanFactory();
        factory.setBeanDefinedList(beanList);
        // 3、开发人员向BeanFactory索要实例对象
        Student student = (Student) factory.getBean("student");
        System.out.println(student);
    }
}测试结果截图:
从代码里面可以看出来,我们是没有直接new学生类的,主要的思想是,定义BeanDefined对象添加进集合中,通过BeanFactory为我们生产出需要的对象,其中用到的核心技术就是:反射
用代码实现简单的依赖注入:
BeanDefined类需要做一定的修改:
public class BeanDefined {
    // bean的id
    private String beanId;
    // bean的文件路径
    private String classPath;
    // 存放属性的集合
    private Map<String, String> propertyMap = new HashMap<>();
    
    // ....省略set和get方法
}BeanFactory类需要做如下的修改:
public class BeanFactory {
    // 存放bean的集合
    private List<BeanDefined> beanDefinedList;
    public List<BeanDefined> getBeanDefinedList() {
        return beanDefinedList;
    }
    public void setBeanDefinedList(List<BeanDefined> beanDefinedList) {
        this.beanDefinedList = beanDefinedList;
    }
    /**
     * 获取bean实例
     *
     * @param beanId
     * @return
     * @throws Exception
     */
    public Object getBean(String beanId) throws Exception {
        Object instance;
        for (BeanDefined bean : beanDefinedList) {
            if (beanId.equals(bean.getBeanId())) {
                String classPath = bean.getClassPath();
                Class classFile = Class.forName(classPath);
                // 在spring中调用默认的构造方法,这里我们也调用默认的构造方法
                instance = classFile.newInstance();
                // 获取bean的属性配置
                Map<String, String> propertyMap = bean.getPropertyMap();
                if(propertyMap !=null) {
                    setValue(instance, classFile, propertyMap);
                }
                return instance;
            }
        }
        return null;
    }
    /**
     * 依赖注入的方法
     *
     * @param instance    当前的实例对象
     * @param classFile   当前实例对象所关联的类文件
     * @param propertyMap 属性
     */
    public void setValue(Object instance, Class classFile, Map<String, String> propertyMap) throws Exception {
        if(propertyMap !=null ) {
            /***
             * 获取map的所有属性配置
             */
            Set<String> proper = propertyMap.keySet();
            for(String string : proper) {
                // 通过字符串拼接,拼出set方法名
                char c = string.toUpperCase().charAt(0);
                String s = "set" + c + string.substring(1);
                // 获取当前属性的类型
                Field field = classFile.getDeclaredField(string);
                // 根据属性的类型进行调用
                Method m = instance.getClass().getMethod(s, field.getType());
                /**
                 * 直接try注入普通类型,或者catch注入bean工厂中的其他类型
                 */
                try {
                    m.invoke(instance, propertyMap.get(string));
                } catch (Exception e) {
                    m.invoke(instance, getBean(propertyMap.get(string)));
                }
            }
        }
    }
}测试类代码需要做如下修改:
public class Test {
    public static void main(String[] args) throws Exception {
        // 1、声明注册bean
        BeanDefined beanObj = new BeanDefined();
        beanObj.setBeanId("student");
        beanObj.setClassPath("com.pojo.Student");
        // 设置 property
        Map<String, String> propertyMap = beanObj.getPropertyMap();
        propertyMap.put("name", "kxm");
        propertyMap.put("age", "22岁");
        propertyMap.put("teacher", "teacher");
        // 注册教师类
        BeanDefined teacher = new BeanDefined();
        teacher.setBeanId("teacher");
        teacher.setClassPath("com.pojo.Teacher");
        List<BeanDefined> beanList = new ArrayList<BeanDefined>();
        beanList.add(beanObj);
        beanList.add(teacher);
        // 2、声明一个BeanFactory,类似于Spring中的ApplicationContext
        BeanFactory factory = new BeanFactory();
        factory.setBeanDefinedList(beanList);
        // 3、开发人员向BeanFactory索要实例对象
        Student student = (Student) factory.getBean("student");
        System.out.println(student);
    }
}测试结果截图:
仔细分析代码,我们可以发现,没有显示的new对象,也没用用set方法去赋值,但是模拟出来了依赖注入的效果,这也是Spring中依赖注入的原理,当然这里是最简单的实现,剩下的路,还需要骚年你自己走哇~
什么是控制反转(IoC)?什么是依赖注入(DI)?以及实现原理的更多相关文章
- 关于.NET中的控制反转(三)- 依赖注入之 Autofac
		一.Autofac简介 Autofac和其他容器的不同之处是它和C#语言的结合非常紧密,在使用过程中对你的应用的侵入性几乎为零,更容易与第三方的组件集成.Autofac的主要特性如下: 组件侵入性为零 ... 
- Spring   什么是 IOC 控制反转 ?什么是依赖注入?spring的用处 好处 为什么要用
		韩梦飞沙 韩亚飞 313134555@qq.com yue31313 han_meng_fei_sha Spring是一个开源的控制反转(Inversion of Control ,IoC)和 ... 
- ASP.NET中IOC容器Autofac(依赖注入DI 控制反转IOC)
		IOC的一个重点是在程序运行中,动态的向某个对象提供它所需要的其他对象.这一点是通过DI来实现的.Autofac则是比较流行的一款IOC容器. IoC和DI有什么关系呢?其实它们是同一个概念的不同角度 ... 
- 关于.NET中的控制反转(二)- 依赖注入之 MEF
		一.MEF是什么 Managed Extensibility Framework (MEF) 是用于创建可扩展的轻量级应用程序的库. 它让应用程序开发人员得以发现和使用扩展且无需配置. 它还让扩展开发 ... 
- 控制反转IOC与依赖注入DI  -  理论篇
		学无止境,精益求精 十年河东十年河西,莫欺少年穷 昨天是五一小长假归来上班的第一天,身体疲劳,毫无工作热情.于是就看看新闻,喝喝茶,荒废了一天 也就在昨天,康美同事张晶童鞋让我学习下IOC的理论及实现 ... 
- ASP.NET MVC 中 Autofac依赖注入DI 控制反转IOC  了解一下
		先简单了解一这个几个 名词的意思. 控制反转(IOC) 依赖注入(DI) 并不是某种技术. 而是一种思想.一种面向对象编程法则 什么是控制反转(IOC)? 什么是依赖注入(DI) 可以点击下面链接 ... 
- 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, ... 
- 依赖注入 DI 控制反转 IOC MD
		Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ... 
- 控制反转(IoC)与依赖注入(DI)
		前言 最近在学习Spring框架,它的核心就是IoC容器.要掌握Spring框架,就必须要理解控制反转的思想以及依赖注入的实现方式.下面,我们将围绕下面几个问题来探讨控制反转与依赖注入的关系以及在Sp ... 
- 依赖注入 DI 控制反转 IOC 概念 案例 MD
		Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ... 
随机推荐
- pip未找到
			命令终端运行 sudo easy_install pip 安装成功后最后会显示 Installed /Library/Python/2.7/site-packages/pip-9.0.1-py2.7. ... 
- PageHelper支持GreenPlum
			greenplum是pivotal在postgresql的基础上修改的一个数据库,语法和postgresql通用.使用PageHelper做分页插件的时候,发现目前没有针对greenplum做支持,但 ... 
- Unity中数据的存储与交互的初步分析(PlayerPrefs,Dictionary,JsonUnility)
			1.PlayerPrefs PlayerPrefs.SetString(key,Value); PlayerPrefs.GetString(key,Value);字符串类型 PlayerPref ... 
- django OperationalError: unable to open database file 创建数据库
- 自由切换 网页上的   ico   图标
			自由切换 网页上的 ico 图标: 第一步: 进入这个网站 :https://www.uupoop.com/ico/?action=make 第二步: 进入网站后,然后选择 ... 
- oracle 索引失效原因_汇总
			1) 没有查询条件,或者查询条件没有建立索引 2) 在查询条件上没有使用引导列 3) 查询的数量是大表的大部分,应该是30%以上. 4) 索引本身失效 5) 查询条件使用函数在索引列上,或者对索引列进 ... 
- Springboot在包含有参构造方法的类中使用@Value注解取值
			我们在Springboot中经常使用@Value注解来获取配置文件中的值,像下面这样 @Component class A { @Value("${user.value}") pr ... 
- Spring IoC 自定义标签解析
			前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 本篇文章主要介绍 Spring IoC 容 ... 
- C#数据结构与算法系列(十九):选择排序算法(SelectSort)
			1.介绍 选择排序算法属于内部排序算法,是从欲排序的数据中,按指定的规则选出某一元素,再依规定交换位置达到排序的目的 时间复杂度:O(n^2) 双层for 2.思想 选择排序(select sorti ... 
- 无题II HDU - 2236 【二分图+二分答案】
			题目 这是一个简单的游戏,在一个n*n的矩阵中,找n个数使得这n个数都在不同的行和列里并且要求这n个数中的最大值和最小值的差值最小. Input 输入一个整数T表示T组数据. 对于每组数据第一行输入一 ... 
