在Eclipse RCP中实现反转控制(IoC)

Eclipse富客户平台(RCP)是一个功能强大的软件平台,它基于插件间的互连与协做,容许开发人员构建通用的应用程序。RCP使开发人员能够集中精力进行应用程序业务代码的开发,而不须要花费时间从新发明轮子编写应用程序管理的逻辑。

  反转控制(Inversion of Control, IoC)和依赖注入(Dependency Injection, DI)是两种编程模式,可用于减小程序间的耦合。它们遵循一个简单的原则:你不要建立你的对象;你描述它们应当如何被建立。你不要实例化你的部件所须要对象或直接定位你的部件所须要的服务;相反,你描述哪一个部件须要哪些服务,其它人(一般是一个容器)负责将它们链接到一块儿。这也被认为是好莱坞法则:don't call us--we'll call you。

  本文将描述一个简单的方式在Eclipse RCP应用程序中使用依赖注入。为了不污染Eclipse 平台的基础结构以及透明地在RCP之上添加IoC框架,咱们将结合使用运行时字节码操做技术(使用 ObjectWeb ASM库)、Java类加载代理(使用java.lang.instrument包)以及Java annotation。

   什么是Eclipse富客户平台?

  用一句话来说,富客户平台是一个类库、软件框架的集合,它是一个用于构建单机和连网应用程序的运行时环境。

  尽管Eclipse被认为是构建集成开发环境(IDE)的框架,从3.0开始,Eclipse整个产品进行了重构,分割成各类不一样的部件,它些部件能够用于构建任意的应用程序。其中的一个子集构成了富客户平台,它包含如下元素:基本的运行时环境、用户界面组件(SWT和JFace)、插件以及 OSGI层。图1显示了Eclipse平台的主要部件。

Eclipse平台的主要部件
图1. Eclipse平台的主要部件

  整个Eclipse平台是基于插件和扩展点。一个 插件是一个能够独立开发和发布的最小的功能单元。它一般打包成一个 jar文件,经过添加功能(例如,一个编辑器、一个工具栏按钮、或一个编译器)来扩展平台。整个平台是一个相互链接和通讯的插件的集合。一个 扩展点是一个互相链接的端点,其它插件能够用它提供额外的功能(在Eclipse中称为 扩展)。扩展和扩展点定义在XML配置文件中,XML文件与插件捆绑在一块儿。

  插件模式增强了关注分离的概念,插件间的强链接和通信须要经过配线进行设置它们之间的依赖。典型的例子源自须要定位应用程序所须要的单子服务,例如数据库链接池、日志处理或用户保存的首选项。反转控制和依赖注入是消除这种依赖的可行解决方案。

   反转控制和依赖注入

  反转控制是一种编程模式,它关注服务(或应用程序部件)是如何定义的以及他们应该如何定位他们依赖的其它服务。一般,经过一个容器或定位框架来得到定义和定位的分离,容器或定位框架负责:
  • 保存可用服务的集合
  • 提供一种方式将各类部件与它们依赖的服务绑定在一块儿
  • 为应用程序代码提供一种方式来请求已配置的对象(例如,一个全部依赖都知足的对象), 这种方式能够确保该对象须要的全部相关的服务均可用。
  现有的框架实际上使用如下三种基本技术的框架执行服务和部件间的绑定:
  • 类型1 (基于接口): 可服务的对象须要实现一个专门的接口,该接口提供了一个对象,能够从用这个对象查找依赖(其它服务)。早期的容器Excalibur使用这种模式。
  • 类型2 (基于setter): 经过JavaBean的属性(setter方法)为可服务对象指定服务。HiveMind和Spring采用这种方式。
  • 类型3 (基于构造函数): 经过构造函数的参数为可服务对象指定服务。PicoContainer只使用这种方式。HiveMind和Spring也使用这种方式。
  咱们将采用第二种方式的一个变种,经过标记方式来提供服务(下面示例程序的源代码能够在资源部分获得)。 声明一个依赖能够表示为:
@Injected public void aServicingMethod(Service s1, AnotherService s2) { 
  // 将s1和s2保存到类变量,须要时可使用 
}
  反转控制容器将查找Injected注释,使用请求的参数调用该方法。咱们想将IoC引入Eclipse平台,服务和可服务对象将打包放入Eclipse插件中。插件定义一个扩展点 (名称为com.onjava.servicelocator.servicefactory),它能够向程序提供服务工厂。当可服务对象须要配置时,插件向一个工厂请求一个服务实例。ServiceLocator类将完成全部的工做,下面的代码描述该类(咱们省略了分析扩展点的部分,由于它比较直观):
public static void service(Object serviceable) throws ServiceException { 
        ServiceLocator sl = getInstance(); 
        if (sl.isAlreadyServiced(serviceable)) { 
            // prevent multiple initializations due to 
            // constructor hierarchies 
            System.out.println("Object " + serviceable 
                    + " has already been configured "); 
            return; 
        } 

        System.out.println("Configuring " + serviceable); 

        // Parse the class for the requested services 
        for (Method m : serviceable.getClass().getMethods()) { 
            boolean skip = false; 
            Injected ann = m.getAnnotation(Injected.class); 
            if (ann != null) { 
                Object[] services = new Object[m.getParameterTypes().length]; 
                int i = 0; 

                for (Class<?> class : m.getParameterTypes()) { 
                    IServiceFactory factory = sl.getFactory(class, ann 
                            .optional()); 
                    if (factory == null) { 
                        skip = true; 
                        break; 
                    } 
                    Object service = factory.getServiceInstance(); 

                    // sanity check: verify that the returned 
                    // service's class is the expected one 
                    // from the method 
                    assert (service.getClass().equals(class) || class 
                            .isAssignableFrom(service.getClass())); 

                    services[i++] = service; 
                } 

                try { 
                    if (!skip) 
                        m.invoke(serviceable, services); 
                } catch (IllegalAccessException iae) { 
                    if (!ann.optional()) 
                        throw new ServiceException( 
                                "Unable to initialize services on " 
                                        + serviceable + ": " + iae.getMessage(), iae); 
                } catch (InvocationTargetException ite) { 
                    if (!ann.optional()) 
                        throw new ServiceException( 
                                "Unable to initialize services on " 
                                        + serviceable + ": " + ite.getMessage(), ite); 
                } 
            } 
        } 

        sl.setAsServiced(serviceable); 
    }

  因为服务工厂返回的服务可能也是可服务对象,这种策略容许定义服务的层次结构(然而目前不支持循环依赖)。

“七”乐无穷,尽在新浪新版博客,快来体验啊~~~请点击进入~html

相关文章
相关标签/搜索