IoC容器总结与简单模拟

当一个组件需要外部资源时,最直接也最明智的方法是执行查找,这种行为称为主动查找。但这种查找存在一个缺点——组件需要知道如何获得资源。那么它的解决方案是什么呢?请看下文。

AD: 2013大数据全球技术峰会低价抢票中

当一个组件需要外部资源时,最直接也最明智的方法是执行查找,这种行为称为主动查找。但这种查找存在一个缺点——组件需要知道如何获得资源。一个好的获取资源的解决方案是应用IoC(Inversion of Control,控制反转)。它的思想是反转资源获取的方向。传统的资源查找方式是要求组件向容器发起请求来查找资源,作为回应,容器适时的返回资源。而应用了IoC之后,则是容器主动的将资源推送到它所管理的组件里,组件所要做的仅仅是选择一种合适的方式接受资源。

IoC是一种通用的设计原则,而DI(Dependency Injection,依赖注入)则是具体的设计模式,它体现了IoC的设计原则。DI是IoC典型的实现,所以IoC与DI术语会被混用。IoC与DI的关系就好比Java中的"接口"和"接口的实现类"的关系一样。

在DI模式下,容器全权负责的组件的装配,容器以一些预先定义好的方式(例如setter方法或构造函数)将匹配的资源注入到每个组件里。目前有三种类型的DI:

setter注入,setter注入会存在一些问题,1. 容易出现忘记调用setter方法注入组件所需要的依赖,将会导致NullPointerException异常。2. 代码会存在安全问题,第一次注入后,不能阻止再次调用setter,除非添加额外的处理工作。但是由于setter注入非常简单所以非常流行(绝大多数Java IDE都支持自动生成setter方法)。
构造器注入,构造器注入能够一定程度上解决setter注入的问题。但是该中注入方式也会带来一些问题,如果组件有很多的依赖,则构造函数的参数列表将变得冗长,会降低代码可读性。
接口注入 ,该注入方式使用的非常少,它要求组件必须实现某个接口,容器正是通过这个接口实现注入依赖的。接口注入的缺点比较明显,使用接口注入需要实现特定的接口,而接口又特定于容器,所以组件对容器产生了依赖,一旦脱离容器,组件不能重用。这是一种"侵入式"注入。

其中"setter注入"和"构造器注入"是被广泛运用的,绝大多数的IoC容器都支持这两种DI类型。

模仿Spring IoC容器

假设一个系统的功能之一是能够生成PDF或HTML格式的报表。

  1. /*生成报表的通用接口*/
  2. public interface ReportBuilder
  3. {
  4. public void build(String data);
  5. }

生成PDF和HTML格式的实现类:

  1. /*生成HTML格式报表*/
  2. public class ReportHtmlBuilder implements ReportBuilder {
  3. @Override
  4. public void build(String data) {
  5. /*示意代码*/
  6. System.out.println("build html report!");
  7. }
  8. }
  9. /*生成PDF格式报表*/
  10. public class ReportPdfBuilder implements ReportBuilder {
  11. @Override
  12. public void build(String data) {
  13. System.out.println("build pdf report!");
  14. }
  15. }

报表服务类:

  1. /*报表服务类*/
  2. public class ReportService
  3. {
  4. /*依赖"ReportBuilder"*/
  5. private ReportBuilder builder;
  6. public ReportBuilder getBuilder()
  7. {
  8. return builder;
  9. }
  10. /*setter注入*/
  11. public void setBuilder(ReportBuilder builder)
  12. {
  13. this.builder = builder;
  14. }
  15. public void builderYearReport(int year)
  16. {
  17. this.builder.build("data");
  18. }
  19. }

IoC容器配置文件"component.properties"

  1. pdfBuilder=com.beliefbitrayal.ioc.inter.imp.ReportPdfBuilder
  2. htmlBuilder=com.beliefbitrayal.ioc.inter.imp.ReportHtmlBuilder
  3. reportService=com.beliefbitrayal.ioc.server.ReportService
  4. reportService.builder=htmlBuilder

IoC容器:

  1. public class Container
  2. {
  3. /*用于储存Component的容器*/
  4. private Map<String, Object> repository = new HashMap<String, Object>();
  5. public Container()
  6. {
  7. try
  8. {
  9. /*读取容器配置文件"component.properties"*/
  10. Properties properties = new Properties();
  11. properties.load(new FileInputStream("src/component.properties"));
  12. /*获取配置文件的每一行信息*/
  13. for(Map.Entry<Object, Object> entry : properties.entrySet())
  14. {
  15. String key = (String)entry.getKey();
  16. String value = (String)entry.getValue();
  17. /*处理配置文件的每一行信息*/
  18. this.handler(key, value);
  19. }
  20. }
  21. catch (Exception e)
  22. {
  23. e.printStackTrace();
  24. }
  25. }
  26. private void handler(String key,String value) throws Exception
  27. {
  28. /*
  29. * reportService=com.beliefbitrayal.ioc.server.ReportService
  30. * reportService.builder=htmlBuilder
  31. * 第一种情况,key值中间没有"."说明为一个新组件。对它的处理为创建它的对象,将其对象放入Map中。
  32. * 第二种情况,key值中间出现"."说明这个属性条目是一个依赖注入。根据"."的位置将这个key值划分为两部分,第一部分为组件的名字,第二部分为
  33. * 该组件需要设置的属性。
  34. */
  35. String[] parts = key.split("\\.");
  36. /*情况1*/
  37. if(parts.length == 1)
  38. {
  39. /*通过反射的方式创建组件的对象*/
  40. Object object = Class.forName(value).newInstance();
  41. this.repository.put(key, object);
  42. }
  43. else
  44. {
  45. /*对于情况2,首先用key值的第一部分(组件名)获取组件*/
  46. Object object = this.repository.get(parts[0]);
  47. /*再使用value值指定的组件名从Map对象中获取依赖*/
  48. Object reference = this.repository.get(value);
  49. /*将获取的依赖注入到指定的组件的相应属性上,"PropertyUtils"类属于Apache下Commons BeanUtil第三方类库,
  50. * 要使用它还需要下载Commons Logging第三方类库
  51. */
  52. PropertyUtils.setProperty(object, parts[1], reference);
  53. }
  54. }
  55. public Object getComponent(String key)
  56. {
  57. return this.repository.get(key);
  58. }
  59. }

根据配置文件,我们在场景类中使用的报表应该是HTML格式的:

  1. public class Client
  2. {
  3. public static void main(String[] args)
  4. {
  5. /*创建容器*/
  6. Container container = new Container();
  7. /*从容器中获取"报表服务类"*/
  8. ReportService reportService = (ReportService)container.getComponent("reportService");
  9. /*显示报表*/
  10. reportService.builderYearReport(0);
  11. }
  12. }

控制台的输出:

  1. build html report!

我们若需要PDF格式的只需要修改属性文件即可:

  1. pdfBuilder=com.beliefbitrayal.ioc.inter.imp.ReportPdfBuilder
  2. htmlBuilder=com.beliefbitrayal.ioc.inter.imp.ReportHtmlBuilder
  3. reportService=com.beliefbitrayal.ioc.server.ReportService
  4. reportService.builder=pdfBuilder

场景类不变,控制台输出:

  1. build pdf report!

容器可以从基于文本的控制文件中读取组件的定义,这使得容器可以重用。现在即使随意改变组件的定义,都不用修改容器的代码。这个例子很好的演示了IoC容器的核心原理和机制。

通过以上分析和举例,控制反转IoC就是一个组件的依赖是由容器来装配,组件不做定位查询,只提供普通的Java方法让容器去装配依赖关系,IoC容器是一般通过setter注入或构造函数注入的方式将依赖注入到组件中的,组件的依赖我们一般通过一个配置文件来描述(XML或Properties),配置文件在IoC容器被构建时读取解析。

IoC容器总结与简单模拟相关推荐

  1. 框架:简单实现Spring的IOC容器

    学习过Spring的同学都知道,Spring框架的核心就是IoC和AOP.Spring可以理解为一个工厂,负责对象的创建和对象间关系的维护.IoC即控制反转,简单点说就是原来的对象是在要使用之前通过在 ...

  2. 比Spring简单的IoC容器

    比Spring简单的IoC容器 Spring 虽然比起EJB轻量了许多,但是因为它需要兼容许多不同的类库,导致现在Spring还是相当的庞大的,动不动就上40MB的jar包, 而且想要理解Spring ...

  3. 抛开 Spring 去理解 IOC 思想:原来 IOC 容器这么简单

    很多小伙伴们看到标题可能就会想到抛开 Spring 就不会存在 IOC 思想了,其实不然在接下来的文章中就会讲述到. 很多小伙伴在理解 IOC 的时候通常会和 Spring 放到一起去学习,首先呢 S ...

  4. ioc spring 上机案例_抛开Spring去理解IOC思想 - 原来IOC容器这么简单

    很多小伙伴们看到标题可能就会想到抛开Spring就不会存在IOC思想了,其实不然在接下来的文章中就会讲述到. 很多小伙伴在理解IOC的时候通常会和Spring放到一起去学习,首先呢Spring设计的非 ...

  5. IoC容器Autofac(2) - 一个简单示例(附demo源码)

    上篇文章中(IoC容器Autofac(1) -- 什么是IoC以及理解为什么要使用Ioc),我们用自己的方式实现了一个简陋的工厂类来实现IoC. 这里我们尝试使用Auotfac来替换我们的工厂类Mov ...

  6. 应用集成——数据库集成与简单实现IoC容器

    应用集成--数据库集成与简单实现IoC容器 项目地址:郝凯VioletEverGarden/ioc应用集成 问题描述: ​ 设分别存在两个数据库,每个数据库都有一个学生表,表名与其中的属性名都不相同. ...

  7. 手写一个简单的IOC容器

    手写一个简单的IOC容器 原文 http://localhost:4000/2020/02/25/SSM/spring/%E6%89%8B%E5%86%99%E4%B8%80%E4%B8%AA%E5% ...

  8. 什么是IOC容器——简单明了

    1.IOC不是一种技术,只是一种思想 英文原文:Inversion of Control 中文翻译:控制反转 一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合,更优良的程序.传统应用程序都是 ...

  9. C#写简单的IOC容器

    文章目录 前言 一.IOC和DIP 1.IOC 2.DIP 二.写一个超简单的IOC容器实例 1.代码准备 2.IOC容器 3.调用 结果 总结 前言 IOC个人之前一直搞不明白,不够理解,写这篇文章 ...

最新文章

  1. Product Orders(生产订单)状态相关函数BAPI
  2. 如何用Python实现超级玛丽的界面和状态机?
  3. 数据中心液体冷却技术的“机架经济学”
  4. html怎么建边框,如何使用CSS创建多色边框?
  5. ANN:神经网络堆叠/进化故事( 从感知机到DRBN )
  6. vue锚点定位(代码通用) - 总结篇
  7. react hooks_如何破坏React Hooks的基础
  8. JDBC操作数据库,第一:jsp插入mysql数据库,坎坷摸索分享
  9. expected at least 1 bean which qualifies as autowire candidate for this depe (spring无法注入)...
  10. idean中jsp页面乱码_IntelliJ IDEA 控制台 乱码 有效解决办法
  11. phpstudy打开浏览php页面发现显示源码解决方法
  12. NAS存储技术之NAS的结构
  13. mysql ndb安装_MySQL NDB Cluster 安装文档-基于CentOS7搭建
  14. 《假如给我三天光明》读后感及其摘录(2)
  15. 车辆搜索 -使用triplet loss 训练车辆识别模型
  16. word怎么拆分表格
  17. Docker自动化部署安装(五)之安装portainer-docker容器管理工具
  18. 史上最牛的论坛推广方法,论坛推广实战方案!
  19. MySQL高可用解决方案
  20. 【题解】A1188 KKT数组201走楼梯

热门文章

  1. ansible自动化运维(二)——环境部署及常用模块的使用
  2. MySQL数据库-操作基础
  3. java原子变量的作用_AtomicInteger原子类的作用介绍(代码示例)
  4. java观察者模式类图_设计模式(十八)——观察者模式(JDK Observable源码分析)...
  5. centos格式化优盘命令_centos 磁盘分区、格式化及挂载
  6. 挡d挡切换_新手司机请问开车可以直接从“D档”挂到“S档”吗?
  7. oracle or条件后 排序,WHERE条件和排序
  8. 各种抠图动态图片_学习抠取动物毛发图片的PS抠图技巧
  9. qlabel 边加载边更新_普及一下什么是超窄边液晶拼接屏
  10. android 禁用组件,android