【SSH进阶之路】一步步重构容器实现Spring框架——解决容器对组件的“侵入式”管理的两种方案--主动查找和控制反转(九)

来源:互联网 发布:淘宝售后客服工资算法 编辑:程序博客网 时间:2024/06/05 16:45


对于IOC的原理,我们曾经写过一篇博文,【SSH进阶之路】Spring的IOC逐层深入——为什么要使用IOC[实例讲

解](二),对比学习可以对同一个问题理解的更加深刻。

       上篇博文【SSH进阶之路】一步步重构容器实现Spring框架——从一个简单的容器开始(八),我们为了去掉接口

对具体实现的依赖关系,封装了一个特别简陋的容器,最后给大家抛出了一个问题:如何让组件不再依赖容器?这篇

博文主要是通过两种解决方案来解决这个问题,最后对比各自的优缺点。


服务定位器


       解决方案之一就是使用服务定位器(Service Locator),我们也可以叫主动查找。服务定位器用来封装复杂的查

找逻辑,同时对外开放简单的查找方法,所有组件都可以将查找请求委派给服务定位器。

       服务定位器可是一个简单的类,也可以是一种复杂的机制,如JNDI。不同的容器有着不同的查找机制。

下面是一个简单的服务定位器:

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. public class ServiceLocator {  
  2.       
  3.     static{  
  4.         //该类加载的时候执行一次  
  5.         Container.init();  
  6.     }  
  7.     public static Object getDao(){  
  8.         return Container.getComponent("dao4Mysql");  
  9. //      return Container.getComponent("dao4Oracle");  
  10.     }  
  11. }  
修改ServiceImpl的查找逻辑:

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. import com.tgb.container.ServiceLocator;  
  2. import com.tgb.container.dao.Dao;  
  3. import com.tgb.container.service.Service;  
  4.   
  5. public class ServiceImpl implements Service {  
  6.     // 从服务器定位器查找所需的接口  
  7.     private Dao dao = (Dao) ServiceLocator.getDao();;    
  8.       
  9.     @Override  
  10.     public void serviceMethod() {  
  11.         dao.daoMethod();  
  12.     }  
  13.   
  14. }  
UML类图:

        

        原先由ServiceImpl到Container的依赖线上添加了ServiceLocator,组件不再直接依赖于容器,实现了“非侵入

式”管理。


控制反转(IoC)


       解决方案之二就是使用控制反转,我们将控制权交给容器,在运行期才由容器决定将具体的实现动态的“注入”到

调用类的对象中。

       Ioc是一种通用的设计原则,DI(依赖注入)则是具体的设计模式。依赖注入有三种方式,我们使用的是Setter注入。


修改Service接口:

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. import com.tgb.container.dao.Dao;  
  2.   
  3.   
  4. public interface Service {  
  5.     //增加注入接口的方法  
  6.     public void setDao(Dao dao);  
  7.     public void serviceMethod();  
  8. }  
修改ServiceImpl:

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. import com.tgb.container.dao.Dao;  
  2. import com.tgb.container.service.Service;  
  3.   
  4. public class ServiceImpl implements Service {  
  5.     private Dao dao;    
  6.     //依赖注入  
  7.     public void setDao(Dao dao) {  
  8.         this.dao= dao;  
  9.     }  
  10.       
  11.     @Override  
  12.     public void serviceMethod() {  
  13.         dao.daoMethod();  
  14.     }  
  15. }  
修改Container类的初始化方法:

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. import java.util.HashMap;  
  2. import java.util.Map;  
  3.   
  4. import com.tgb.container.dao.Dao;  
  5. import com.tgb.container.dao.impl.Dao4MySqlImpl;  
  6. import com.tgb.container.service.Service;  
  7. import com.tgb.container.service.impl.ServiceImpl;  
  8.   
  9. public class Container {  
  10.     private static Map<String, Object> components;  
  11.   
  12.     private Container() {  
  13.     }  
  14.   
  15.     /** 
  16.      * 初始化容器 
  17.      */  
  18.     public static synchronized void init() {  
  19.         if (components == null) {  
  20.             components = new HashMap<String, Object>();  
  21.               
  22.             //写一个读配置文件的类,根据读取的配置文件,反射对应的类  
  23.             //反射好类后进行 依赖管理,往对应的属性上注入相应的类  
  24.               
  25.             Dao dao4Mysql = new Dao4MySqlImpl();  
  26.             components.put("dao4Mysql", dao4Mysql);  
  27.               
  28.             Service service = new ServiceImpl();    
  29.             components.put("service", service);  
  30.               
  31.             //容器维护依赖关系  
  32.             service.setDao(dao4Mysql);  
  33.         }  
  34.     }  
  35.   
  36.     /** 
  37.      * 查找组件 
  38.      *  
  39.      * @param id 
  40.      * @return 
  41.      */  
  42.     public static Object getComponent(String id) {  
  43.         return components.get(id);  
  44.     }  
  45. }  
UML类图:

        

       由ServiceImpl到Container的依赖线可以直接抹掉了!

       Setter注入易于使用,但是会有安全问题。第一次注入之后,有可能再一次调用setter方法,改变了原有的依赖。

这种对依赖的无意修改会带来无法预料的后果。所以需要有安全检查机制。


对比


       解决组件不再依赖容器,我们使用了两种方案:服务定位器和控制反转。

       1、使用服务定位器查找组件,这是一种主动查找的行为。这种查找有一个缺点:组件需要知道如何查找资源。组件和容器依赖变成了组件和服务定位器的依赖。

       2、然后,我们使用了控制反转,这是一种被动查找的行为。容器主动将资源推送给组件,组件则以一种合适的方式来接受资源。反转资源获取方向,这就是大名鼎鼎的Ioc(控制反转)。


       从类图中我们可以发现,容器需要知道各个组件,容器和组件的耦合度还是很高的,下篇博文【SSH进阶之路】 

一步步重构容器实现Spring框架——配置文件+反射实现IoC容器(十),我们利用配置文件+反射进一步降低耦合度。

0 0