Google Guice实战

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