• 2007-05-31

    Google Guice Get-Starting - [Java]

    Tag:Java

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://navigating.blogbus.com/logs/5587688.html

    Guice(pronounced "juice")发布了1.0版本.
    首先看看Guice的定位:an ultra-lightweight, next-generation dependency injection container for Java 5 and later.
    这是一个基于Java 5及后续版本的轻量级依赖注入容器。Martin Fowler关于依赖注入的精彩讨论参见:http://www.martinfowler.com/articles/injection.html#InversionOfControl
    在这篇讨论中涉及到几种注入方式:Constructor Injection、Setter Injection、Interface Injection;
    实际应用开发中依赖的注入问题自始至终都伴随着我们,当我们走过了最简单直接的手动注入、运用模式进行注入,到依赖于框架,当前成熟的框架众多,例如:Spring IOC、PicoContainer、Hivemind等;那么Guice作为一个依赖注入的framework,又是怎么定位自己的呢?其special的地方又在哪儿呢?我们首先看一个非常简单的例子:
    对于注入描述的类如下:
    public class MyModule implements Module {
    public void configure(Binder binder) {
    binder.bind(Service.class).to(ServiceImpl.class).in(Scopes.SINGLETON);
    }
    }
    依赖注入发生的地方:
    public class Client {

    private final Service service;

    @Inject
    public Client(Service service) {
    this.service = service;
    }

    public void go() {
    service.go();
    }
    }
    从这个例子我们可以清晰看出,Guice的依赖注入已经不同于以往的三种注入模式了,Okey,你已经看到了Guice充分使用了Java的新特性annotation来进行依赖注入的,已经完全跨越了Constructor Injection、Setter Injection、Interface Injection的概念了。这样,依赖注入只是需要一个或者多个annotation而已,一切搞定。
    下一步我看看Guice这个Framework是要如何进行依赖注入的?
    我们首先看看Guice的Architecture.
    public class MyModule implements Module {
    public void configure(Binder binder) {
    // Bind Foo to FooImpl. Guice will create a new
    // instance of FooImpl for every injection.
    binder.bind(Foo.class).to(FooImpl.class);

    // Bind Bar to an instance of Bar.
    Bar bar = new Bar();
    binder.bind(Bar.class).toInstance(bar);
    }
    }
    该部分代码的sequence diagram如下:

    运行时Guice的依赖注入的绑定器structure如下:

    很明显的看到,一个注入的绑定关联主键Key、Scope、Provider;其中Key包含了依赖的Type和Annotation.
    看到了Guice的简单绑定,一定会有一个疑问:就是一个类型的多个绑定怎么实现?Guice是运行Annotation binding来解决这个问题的。
    例如绑定代码如下:
    public class BindingModule implements Module {

    public void configure(Binder binder) {
    binder.bind(IService.class).annotatedWith(Blue.class).to(BlueService.class);

    }

    }
    注入代码如下:
    public class BindingClient {
    // @Inject
    // @Blue
    // private IService service;

    private IService service;

    @Inject
    public void injectService(@Blue IService service) {
    this.service = service;
    }

    public void go() {
    this.service.go();
    }
    }
    annotation部分代码:
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.FIELD, ElementType.PARAMETER})
    @BindingAnnotation
    public @interface Blue {

    }
    如果再复杂一些,Guice还支持有属性的annotation:
    绑定代码
    public class NamedModule implements Module {

    /*
    * (non-Javadoc)
    *
    * @see com.google.inject.Module#configure(com.google.inject.Binder)
    */
    public void configure(Binder binder) {
    binder.bind(IPerson.class).annotatedWith(new NamedAnnotation("Bob")).to(Bob.class);
    }

    }
    注入代码:
    public class NamedClient {

    @Inject
    @Named("Bob")
    private IPerson person;

    public void say(){
    this.person.say();
    }
    }
    annotation代码:
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.FIELD, ElementType.PARAMETER})
    @BindingAnnotation
    public @interface Named {
    String value();
    }

    public class NamedAnnotation implements Named {

    private String value;

    public NamedAnnotation(String value){
    this.value = value;
    }
    /*
    * (non-Javadoc)
    *
    * @see com.neusoft.anno.Named#value()
    */
    public String value() {
    return value;
    }

    public int hashCode() {
    return 127 * "value".hashCode() ^ value.hashCode();
    }

    public boolean equals(Object o) {
    if (!(o instanceof Named))
    return false;
    Named other = (Named) o;
    return value.equals(other.value());
    }

    public String toString() {
    return "@" + Named.class.getName() + "(value=" + value + ")";
    }

    /*
    * (non-Javadoc)
    *
    * @see java.lang.annotation.Annotation#annotationType()
    */
    public Class annotationType() {
    return Named.class;
    }

    }
    通过这几个例子,可以清晰看到,Guice应用annotation进行注入的关联的,用module将依赖关系组装起来,提供给客户端代码进行使用;而且annotation是可以自由定制的,支持灵活扩展的。

    Guice本身也支持实现类的直接绑定:
    public class MixerModule implements Module {

    /*
    * (non-Javadoc)
    *
    * @see com.google.inject.Module#configure(com.google.inject.Binder)
    */
    public void configure(Binder binder) {
    binder.bind(Concrete.class);

    }

    }
    Guice有一种隐式绑定,其方法是这样,针对一个接口,如果缺少显式绑定,Guice 会寻找一个指向具体实现的@ImplementedBy标注。
    @ImplementedBy(GoodNightImpl.class)
    public interface IGoodNight {

    void goodNight();
    }

    @Singleton
    public class GoodNightImpl implements IGoodNight {

    /*
    * (non-Javadoc)
    *
    * @see com.neusoft.nobinding.IGoodNight#goodNight()
    */
    public void goodNight() {
    System.out.println("Good night!");
    }

    }

    那么,Provider是怎么回事呢?“有时对于每次注入,客户代码需要某个依赖的多个实例。其它时候,客户可能不想在一开始就真地获取对象,而是等到注入后的某个时候再获取。对于任意绑定类型 T,你可以不直接注入 T 的实例,而是注入一个 Provider,然后在需要的时候调用 Provider.get()”因此,可以使用Provider去真正的来创建依赖的对象实例,直到需要的时候才创建客户代码需要的实例。

    Provider等绑定代码:
    public class WidgetModule implements Module {

    /*
    * (non-Javadoc)
    *
    * @see com.google.inject.Module#configure(com.google.inject.Binder)
    */
    public void configure(Binder binder) {
    binder.bind(IService.class).to(WidgetService.class).in(Scopes.SINGLETON);
    binder.bind(Widget.class).toProvider(WidgetProvider.class);
    }

    }
    Provider的注入:
    public class Widget {
    private IService service;

    @Inject Provider provider;

    public Widget(IService service){
    this.service = service;
    }

    public void go(){
    this.service.go();
    }
    }
    Provider的实现:
    public class WidgetProvider implements Provider {

    final IService service;

    @Inject
    WidgetProvider(IService service) {
    this.service = service;
    }

    public Widget get() {
    return new Widget(service);
    }

    }
    Service的接口和实现:
    public interface IService {

    void go();

    }
    public class WidgetService implements IService {

    /* (non-Javadoc)
    * @see com.neusoft.service.IService#go()
    */
    public void go() {
    System.out.println("Widget Service Hello.");
    }

    }
    客户代码:
    public class WidgetApplication {

    /**
    * @param args
    */
    public static void main(String[] args) {
    Injector injector = Guice.createInjector(new WidgetModule());
    Widget widget = injector.getInstance(Widget.class);
    widget.go();
    }

    }
    这个例子完全表达出了Provider的价值所在。
    另外,Guice也提供了Constant Values以及Converting Stings的支持:
    Guice会针对Primitive types、primitive wrapper types、Strings、Enums、Classes类型进行类型的自动判断绑定机制。对于Primitive types、primitive wrapper types,如果 Guice 仍然无法找到一个的显式绑定,它会去找一个拥有相同绑定标注的常量 String 绑定,并试图将字符串转换到相应的值。
    这就是一个Guice如何进行依赖注入的一个基本的思路,可以看到Guice的代码量十分少,是一个不错的依赖注入的方案,也将会推动依赖注入的进步。
    参考:
    Guice项目:http://code.google.com/p/google-guice/
    Guice中文文档:http://docs.google.com/View?docid=dqp53rg_3hjf3ch


    历史上的今天:


    收藏到:Del.icio.us




    引用地址:

    评论

  • peoiwck mntkps mdujl bikqhv kuzes gzvde mgcntiquz
  • qxcmzdrwv qjvtz ivpd siwhkfg bwuxrhz xnuahi zvybeolh