Chinaunix首页 | 论坛 | 博客
  • 博客访问: 30480285
  • 博文数量: 708
  • 博客积分: 12163
  • 博客等级: 上将
  • 技术积分: 8240
  • 用 户 组: 普通用户
  • 注册时间: 2007-12-04 20:59
文章分类

全部博文(708)

分类: Java

2008-10-14 15:28:12

题外语:
  我很早就发现了《国宝档案》这个节目中的配乐偶尔会选用仙剑音乐或轩辕剑之天之痕的主题音乐。我还发现《幸福的小河》和《非常道》两部讲述生活的电影比一些所谓的巨片好看得多!

  在中,“ultra-lightweight”、“next-generation”等几个关键词语引起了我的好奇心,所以我决定要花点时间进入它的设计和实现层,目的是为了学习一下这个被Google称为超轻量级的、下一代的依赖注入容器的设计和实现思想。需要预先说明的是,本文并不对如何使用Guice 1.0作介绍,若要了解相关的内容,请访问。到写这篇文章为止,Guice的发行版本是1.0,所以接下来我说的Guice均是指Guice 1.0。而且我不打算把它的每个实现细节都搬出来,而是简单地说说自己对其主要模块的理解。

  Guice说明文档提到,在其架构中分了两个阶段:启动阶段和运行阶段。所以,在我们研究的时候,也可以分两个阶段进行 ——

  【启动阶段】

  这个阶段所做的工作是读取和记录客户制定的规则,利用这些信息创建出Injector实例供客户使用。在这整个阶段中,客户制定的规则是变化的,而Injector实例的创建算法(创建步骤)是稳定的,所以很自然的用到了Builder模式。其中客户关注的当然就是如何去制定的规则,这些规则包括:对象的依赖关系、标注与域的映射关系、拦截器将作用在什么地方等等。后两者相对来说比较简单,所以接下来我着重要说的是最经常被制定的“对象的依赖关系”。它的编程模型很漂亮,形式如下:

    binder.bind(xxx).to(xxx);
    binder.bindConstant().annotatedWith(xxx).toInstance(yyy);
    ...

  可以简单的认为bind和bindConstant方法划分了两类绑定(它们有交集,在Guice说明文档的“Injecting Constant Values”一节中被说明了),这两类绑定的信息分别由下面两种组件记录:

  比如说,当binder.bind(xxx).to(xxx)被调用时,就会有一个BindingBuilderImpl实例被创建,它将用来记录指定类型的Key、绑定目标的内部工厂(内部工厂将在下文提到)、目标实例(实例绑定才会有)、域(有指定才会有)等信息。Binder在创建Injector的时候,会使用这些信息来创建Binding实例,并交由Injector管理。

  在编程模型中我们可以看到,它最明显的一个语意是某类型的依赖从何而来。比如binder.bind(Service.class).to(ServiceImpl.class)表明了依赖是某具体实现的实例;又如binder.bind(Service.class).toProvider(ServiceProvider.class)表明了依赖是某提供者产生的;等等。这在Guice内部实现中做了抽象,那就是内部工厂,任何的依赖都是由某种内部工厂中产生。

  一些内部工厂实现了CreationListener,它们需要知道创建Injector实例的那一刻,从而与其他内部工厂进行“串联”,这表明了它是非终结内部工厂,当然这是我个人的叫法,因为它让我联想到Decorator模式,这里我没有把它的组合关系画出来,如果画出来的话就可以看出它跟Decorator模式的结构是非常相似的。

  【运行阶段】

  这个阶段的主要工作有两个:一、构造客户所需的依赖;二、依赖注入。其实当涉及到预装载和静态注入的时候,这些工作也会在启动阶段中进行,只是我在本文中避开了预装载和静态注入的讨论,避开的原因是我个人认为它们比较容易理解。

  Guice在构造对象的设计上非常巧妙 ——

  首先是Builder模式的利用,客户向Guice注册拦截器是变化的,构造ProxyFactory实例是稳定的,ProxyFactoryBuilder将它们隔离开;其次是DefaultConstructionProxyFactory让java.lang.reflect或cglib的使用变成透明;最后是ProxyFactory让有拦截还是无拦截变成透明。这样的设计似乎让所有麻烦都在间接构造(使用ConstructionProxy接口)中消失了。

  接下来看看具体的各种注入器,它们应该是依赖注入容器的主角吧 ——

  ConstructorInjector用来创建实例,并对其进行依赖注入,其实在对象构造的同时就有可能进行着依赖的注入,那就是所谓的构造器注入。在对象构造完毕后可能还需要进行字段和方法注入。在具体设计中,字段和方法注入器被抽象为SingleMemberInjector,由于构造器和方法的注入都需要获取实参,所以SingleParameterInjector抽取了它们公用的服务。

  依赖注入的过程是个递归的过程,在这个过程中有一些具体的注入器是可以被重复使用的,所以Guice会将它们缓冲起来,从而大大地提高了性能。

  最后,我发现MyElipse UML Feature 5.1.1的反向工程竟然不支持范型,这让我很郁闷...所以上面的类图来之不易,呵呵:-)  —— by kong @ 19:20 2008-3-23

阅读(2737) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~