spring recipes读书笔记--理解IOC原理

假设将开发一个系统,功能之一是生产HTML或PDF格式的报表。
首先创建生产报表的通用接口,然后分别生产HTML格式和PDF格式报表
public interface ReportGenerator{
   public void generate(String[][]table);
}

public class HtmlReportGenerator implements ReportGenerator{
   public void generate(String[][]table){
       System.out.println("generate html report");
   }
}

public class PdfReportGenerator implements ReportGenerator{
    public void generate(String[][] table){
        System.out.println("generate pdf report");
    }
}

接下来创建服务类ReportService,作为生成不同类型报表的服务提供者
public class ReportService{
    private ReportGenerate reportGenerator =new PdfReportGenerator();
    public void generateAnnualReport(int year){
        String[][] statistics =null;
        //set value for statistics
        reportGenerator.generate(statistics);        
    }
}

上面代码是我们一般的实现方法,没有采用IOC(Inversion of Control)设计模式,此时ReportService和ReportGenerator耦合在一起。
为了降低耦合程度,我们可以采用一个容器来管理ReportService和ReportGenerator的依赖关系。
public class Container{
    public static Container instance;
    private Map<String,Object> components;

    public Container(){
        components = new HashMap<String,Object>();
        instance=this;
        ReportGenerator reportGenerator=new PdfReportGenerator():
        components.put("reportGenerator",reportGenerator);
        ReportService reportService=new ReportService();
        components.put("reportService",reportService);
    }

    public Object getComponent(String id){
        return components.get(id);
    }
}

采用上面容器后,使用MAP存储组件,ReportService里不再需要新建ReportGenerator实例了。
public class ReportService{
    private ReportGenerator reportGenerator=(ReportGenerator)   Contain.instance.getComponent("reportGenerator");
    ......
}

此时,ReportService与ReportGenerator解除耦合,ReportService通过Container容器来查找报表生成服务。

但是直到现在,也 只是解除了组件之间的依赖,并没有采用IOC的设计模式。此时,ReportService需要主动的到容器中查找ReportGenerator组件,而不是容器主动的将资源推送给ReportService。
采用依赖注入模式,ReportService需要公开setter方法,用来接受ReportGenerator类型的熟悉
public class ReportService{
    private ReportGenerator reportGenerator;//此时不再需要主动查找reportGenerator实例
     public void setReportGenerator(ReportGenerator reportGenerator){
        this.reportGenerator = reportGenerator;
    }
    ......
}

public class Container{
    //因为组件不再需要主动查找,所有容器不用再提供自身的静态变量了
     private Map<String,Object> components;
    public Container(){
        components = new HashMap<String,Object>();
        ReportGenerator reportGenerator = new PdfReportGenerator();
        components.put("reportGenerator",reportGenerator);
        ReportService reportService=new ReportService();
        reportService.setReportGenerator(reportGenerator);
        components.put("reportService",reportService);
    }
    
    public Object getComponent(String id){
        return components.get(id);
    }
} 

现在ReportService不再向容器主动查找报表生成组件,而是由容器将报表生成组件推送给ReportService自身。
public class IOCTest{
    public static void main(String[] args){
        Container container=new Container();
        ReportService reportService = (ReportService) container.getComponent("reportService");
        reportService.generateAnnualReport(2010);
    }
}

至此,我们已实现了IOC的基本原理
然而在代码中配置容器意味着每次修改配置之后都必须重新编译源代码,效率太低。所以一般都是采用配置文件来管理。
这里采用components.properties属性文件来配置。
#定义新组件reportGenerator
reportGenerator = com.ioc.report.PdfReportGenerator
#定义新组件reportService
reportService = com.ioc.report.ReportService
#给reportService的reportGenerator属性注入reportGenerator实例
reportService.reportGenerator = reportGenerator

利用Commons BeanUtils来读取属性文件
public class Container{
    private Map<String,Object> components;
    public Container(){
        components = new HashMap<String,Object>();
        try{
            Properties props=new Properties();
            props.load(new FileInputStream("components.properties"));
            for(Map.Entry entry : props.entrySet()){
                String key = (String)entry.getKey();
                String value=(String)entry.getValue();
                processEntry(key,value);
            }
        }catch(Exception e){
            throws new RuntimeException(e);
        }
    }

    private void processEntry(String key,String value) throws Exception{
        String[] parts=key.split("\\.");
        if(parts.length == 1){
           [color=red]//新建组件[/color]             Object component = Class.forName(value).newInstace();
           components.put(parts[0],component);
        }else{
            [color=red]//依赖注入[/color]             Object component = components.get(parts[0]);
           Object reference = components.get(value);
           PropertyUtils.setProperty(component,parts[1],reference);
        }
    }

    public Object getComponent(String id){
        return components.get(id);
    }
}

现在容器可以从基于文本的配置文件中读取组件定义了,当组件定义改变,如需要HtmlReportGenerator时,不用修改一行java代码。

你可能感兴趣的:(设计模式,spring,配置管理,IOC,读书)