控制反转(IOC)的简单实现及原理分析
2012-08-11 14:36
666 查看
题记
之前转载过一篇关于spring依赖注入的文章(猛点这儿),对于spring的ioc有了直观的感受,但有些问题还是没弄明白。比如spring的IOC容器是如何实现的,为什么要用依赖注入这种方法?IOC是如何产生的?为了弄清楚这些问题,最近看的一本书提供了一个很好的例子,它教我们一步一步实现简单的IOC容器!这对于理解IOC很有帮助,在这里和大家分享下,请细看。
Task
打印输出报表,目前有html和pdf两种格式,以后可能增加。一般打印年度报表和季度报表等。
Solution1
提取一个接口ReportGenerator,里面有一个generator()的抽象方法;HtmlReport和PdfReport是这个接口的两个实现类;ReportService对外提供服务,打印报表。以下是三个类的具体实现。
Solution2
为了解决上面的问题,我们引入了Container,也就是容器。因此 HtmlReport、PdfReport、ReportService都是这个容器中的组件(component)。代码如下
ReportService调整为
Problem:当组件需要引用其他数据资源、文件或者其他组件时,需要向容器进行资源请求。这种请求,在系统十分复杂时,很容易造成各组件间的强耦合!
Solution3
传统模式下,当一个组件A需要另一个组件B时,需要在A的代码中new一个B对象。一旦A不需要依赖B而要依赖C时,A就必须更改代码,将new B改为new C。这种模式对于大规模系统来说,简直就是灾难。于是我们引入了控制反转(IOC),我们不需要去请求资源,而是在加载时就将会利用到的资源加载进来。IOC是一种设计原则,从字面意思来说也就是"资源检索的逆转",组件不再需要自己去请求资源了!DI则是实现这一原则的设计模式,它可以通过setter或者构造函数将资源投递(注入)到相应的组件中!显然,Container来实现这一过程~实现代码如下:
Problem:在container中,是采用pdfReport还是htmlReport?要修改代码就得重新编译!所以spring的IOC容器把这些依赖写到配置文件中,这样就不需要不停地编译啦
Solution4
采用xml或者properties等配置文件来实现组件依赖。本例中为了方便实现,采用properties来完成,需要引入commons-beanutils.jar包,需要调用PropertyUtils.setProperty这个方法。
属性文件如下:
#注入pdfReport到容器中,id是"reportGenerator"
reportGenerator=com.ks.spring.impl.PdfReport
#注入reportService到容器中,id是"reportService"
reportService=com.ks.spring.impl.ReportService
#带"."意思是把"reportGenerator"组件注入到
#reportService组件中的"reportGen"属性中!默认调用setReportGen方法
reportService.reportGen=reportGenerator
Container修改如下:
总结
依赖注入(DI)和控制反转(IOC)从字面上看确实不好理解,咋一看会让初学者望而生畏。类似的讲IOC的文章数不胜数,百度Google一大把,作为初学者却很难找到一篇娓娓道来的文章,根本不会理解得多么深入,仅仅会用而已。我从Spring Recipes这本书中学会了如何一步一步实现简单的IOC容器,从而加深对IOC的理解,所以在此分享下~如有不妥之处,也希望大家指正!
(全文完)
之前转载过一篇关于spring依赖注入的文章(猛点这儿),对于spring的ioc有了直观的感受,但有些问题还是没弄明白。比如spring的IOC容器是如何实现的,为什么要用依赖注入这种方法?IOC是如何产生的?为了弄清楚这些问题,最近看的一本书提供了一个很好的例子,它教我们一步一步实现简单的IOC容器!这对于理解IOC很有帮助,在这里和大家分享下,请细看。
Task
打印输出报表,目前有html和pdf两种格式,以后可能增加。一般打印年度报表和季度报表等。
Solution1
提取一个接口ReportGenerator,里面有一个generator()的抽象方法;HtmlReport和PdfReport是这个接口的两个实现类;ReportService对外提供服务,打印报表。以下是三个类的具体实现。
package com.ks.spring.report; public interface ReportGenerator { public void generator(String[][] table); }
package com.ks.spring.report.impl; import com.ks.spring.report.ReportGenerator; public class HtmlReport implements ReportGenerator { @Override public void generator(String[][] table) { // TODO Auto-generated method stub System.out.println("This is Html report!"); } }
package com.ks.spring.report.impl; import com.ks.spring.report.ReportGenerator; public class PdfReport implements ReportGenerator { @Override public void generator(String[][] table) { // TODO Auto-generated method stub System.out.println("This is pdf report!"); } }
package com.ks.spring.report.impl; import com.ks.spring.report.ReportGenerator; public class ReportService { private ReportGenerator reportGen = new PdfReport(); //打印出某一年的报告 public void printAnnualReport(int year){ String[][] statistics = null; // to do some statistics reportGen.generator(statistics); } //打印出某年中的某个月的报告 public void printMonthReort(int year,int month){ String[][] statistics = null; //to do some statistics reportGen.generator(statistics); } }Problem:在ReportService类中直接依赖了ReportGenerator的两个实现类。如果需要打印出html格式的报表,就需要重新修改代码!显然不够灵活,且耦合度高
Solution2
为了解决上面的问题,我们引入了Container,也就是容器。因此 HtmlReport、PdfReport、ReportService都是这个容器中的组件(component)。代码如下
package com.ks.spring.report.impl; import java.util.HashMap; import java.util.Map; import com.ks.spring.report.ReportGenerator; public class Container { //全局变量,是contatiner的引用 public static Container instance; //通过IDs来管理各个组件 private Map<String,Object> components; public Container(){ components = new HashMap<String,Object>(); instance = this; ReportGenerator reportGen = new PdfReport();//输出Pdf components.put("reportGenerator",reportGen); //这个时候components中已经有reportGenerator组件了 ReportService reportSe = new ReportService(); components.put("reportService",reportSe); } public Object getComponent(String id){ return components.get(id); } }
ReportService调整为
public class ReportService { // private ReportGenerator reportGen = new PdfReport(); private ReportGenerator reportGen = (ReportGenerator)Container.instance.getComponent("reportGenerator"); //打印出某一年的报告 public void printAnnualReport(int year){ String[][] statistics = null; // to do some statistics reportGen.generator(statistics); } //打印出某年中的某个月的报告 public void printMonthReort(int year,int month){ String[][] statistics = null; //to do some statistics reportGen.generator(statistics); } }结论:这样做就把Service类中的依赖给去掉了,外部直接引用Service类,当内部进行改变时,外部的引用对象不需要做相应的修改,换句话说,对外部类屏蔽了底层的实现细节。让Container去依赖generaor的实现。
Problem:当组件需要引用其他数据资源、文件或者其他组件时,需要向容器进行资源请求。这种请求,在系统十分复杂时,很容易造成各组件间的强耦合!
Solution3
传统模式下,当一个组件A需要另一个组件B时,需要在A的代码中new一个B对象。一旦A不需要依赖B而要依赖C时,A就必须更改代码,将new B改为new C。这种模式对于大规模系统来说,简直就是灾难。于是我们引入了控制反转(IOC),我们不需要去请求资源,而是在加载时就将会利用到的资源加载进来。IOC是一种设计原则,从字面意思来说也就是"资源检索的逆转",组件不再需要自己去请求资源了!DI则是实现这一原则的设计模式,它可以通过setter或者构造函数将资源投递(注入)到相应的组件中!显然,Container来实现这一过程~实现代码如下:
public class ReportService { // private ReportGenerator reportGen = new PdfReport(); // private ReportGenerator reportGen = (ReportGenerator)Container.instance.getComponent("reportGenerator"); private ReportGenerator reportGen; public void setReportGen(ReportGenerator reportGen){ this.reportGen = reportGen; } //打印出某一年的报告 public void printAnnualReport(int year){ String[][] statistics = null; // to do some statistics reportGen.generator(statistics); } //打印出某年中的某个月的报告 public void printMonthReort(int year,int month){ String[][] statistics = null; //to do some statistics reportGen.generator(statistics); } }Container类更改如下:
public class Container { //不需要这个变量了 // public static Container instance; //通过IDs来管理各个组件 private Map<String,Object> components; public Container(){ components = new HashMap<String,Object>(); // instance = this; ReportGenerator reportGen = new PdfReport();//输出Pdf components.put("reportGenerator",reportGen); //这个时候components中已经有reportGenerator组件了 ReportService reportSe = new ReportService(); reportSe.setReportGen(reportGen);//在这里进行了注入!!!其实注入也没什么神秘的嘛! components.put("reportService",reportSe); } public Object getComponent(String id){ return components.get(id); } }结论:这样基本上实现了一个IOC容器,资源通过setter方法把组件的依赖自动注入进去,再也不需要new对象了。
Problem:在container中,是采用pdfReport还是htmlReport?要修改代码就得重新编译!所以spring的IOC容器把这些依赖写到配置文件中,这样就不需要不停地编译啦
Solution4
采用xml或者properties等配置文件来实现组件依赖。本例中为了方便实现,采用properties来完成,需要引入commons-beanutils.jar包,需要调用PropertyUtils.setProperty这个方法。
属性文件如下:
#注入pdfReport到容器中,id是"reportGenerator"
reportGenerator=com.ks.spring.impl.PdfReport
#注入reportService到容器中,id是"reportService"
reportService=com.ks.spring.impl.ReportService
#带"."意思是把"reportGenerator"组件注入到
#reportService组件中的"reportGen"属性中!默认调用setReportGen方法
reportService.reportGen=reportGenerator
Container修改如下:
public class Container { //通过IDs来管理各个组件 private Map<String,Object> components; public Container(){ components = new HashMap<String,Object>(); try{ Properties proper = new Properties(); proper.load(new FileInputStream("components.properties")); for(Entry entry:proper.entrySet()){ String key = (String)entry.getKey(); String value = (String)entry.getValue(); } }catch(Exception ex){ ex.printStackTrace(); } } public void processEntry(String key,String value) throws Exception{ String[] parts = value.split("\\."); if(parts.length == 1){ //新的组件定义 Object comp = Class.forName(value).newInstance(); components.put(key, comp); }else{ //依赖注入 Object obj = components.get(parts[0]); Object ref = components.get(value); PropertyUtils.setProperty(obj, parts[1], ref);//根据属性名注入相应的组件 } } public Object getComponent(String id){ return components.get(id); } }就这样,属性文件实现了在container中进行依赖注入!
总结
依赖注入(DI)和控制反转(IOC)从字面上看确实不好理解,咋一看会让初学者望而生畏。类似的讲IOC的文章数不胜数,百度Google一大把,作为初学者却很难找到一篇娓娓道来的文章,根本不会理解得多么深入,仅仅会用而已。我从Spring Recipes这本书中学会了如何一步一步实现简单的IOC容器,从而加深对IOC的理解,所以在此分享下~如有不妥之处,也希望大家指正!
(全文完)
相关文章推荐
- 控制反转(IOC)的简单实现及原理分析
- PHP简单的IoC控制反转实现
- Java仿Spring框架IOC控制反转利用反射简单实现(源码)
- PHP简单的IoC控制反转实现
- PHP简单的IoC控制反转实现
- 用PHP实现简单的IoC控制反转
- ADO.NET .net core2.0添加json文件并转化成类注入控制器使用 简单了解 iTextSharp实现HTML to PDF ASP.NET MVC 中 Autofac依赖注入DI 控制反转IOC 了解一下 C# AutoMapper 了解一下
- 用PHP实现简单的控制反转(IOC) 依赖注入(DI),用JSON配置文件
- 【Spring】Spring的IOC(控制反转)/DI(依赖注入)原理(三):Spring启动加载配置文件源码分析
- JAVA菜鸟学习日记——简单代码实现IOC控制反转
- Spring 概念及特点 Spring下载地址 控制反转IoC实现原理
- Sring控制反转(Inversion of Control,Ioc)也被称为依赖注入(Dependency Injection,DI)原理用反射和代理实现
- HashMap的实现原理简单分析
- spring反转控制IOC或依赖注入之简单示例
- 【JfaceTextFramework学习笔记之四】TextViewer实现原理简单分析
- 【简单Web服务器搭建】基于Socket实现的最简单的Web服务器【ASP.NET原理分析】
- springmvc源码分析原理及简单实现
- SpringMVC结合Shiro注解实现权限控制原理分析
- 自定义spring控制反转及简单原理
- IoC原理-使用反射/Emit来实现一个最简单的IoC容器