Guice 小探

谈到 IoC container 第一个想到的就是 spring,google 也推出了自己的 IoC lib,这就是 guice。与 spring 现在什么都有不大一样的是 guice 目标是轻量级的 IoC,因此比较小而专。首先我们还是看看在没有 IoC 容器的时候人们怎么解决初始化的问题。

假定我们现在实现 Foo,它需要 Bar 的功能,比较直观的做法就是 Foo 的 code 里面直接调用 Bar 的构造函数,可是怎么初始化 Bar 其实 Foo 并不关心,因此往往我们把这个任务交给了第三者,通常叫 BarFactory,这个 factory 通过某些方法或者返回唯一的一个 Bar 实例(singleton)或者少数几个(prototype 或者 singleton 的多个的扩展),factory 从这个意义上将构造过程 abstract out,同时也使得 unit test 成为了可能:当 Foo 需要 Bar 的时候,Foo 问一个特殊的 factory 要,我们就能返回 mock 之类的东西了。

DI 原则下我们可以直接在 Foo 里面插入一个 Bar 的引用而不借助 factory,这样 Foo 既不用担心 Bar 是否初始化,测试起来也很容易可以让 IoC container 放个 mock 进去即可。spring 的思路是说这些对象 Foo、Bar 都是 spring 的 bean,有字符串 id 作为标识(打开了 autowiring 的 spring 可以基于别的属性来做),而 guice 则使用了 annotation 的策略。下面是一个简单的例子,我们把前面的 Bar 设置为某个接口,这样可以看到某些情况下怎么用:

package demo ;

public interface IBar {
    void hello () ;
}

实现如下

package demo ;

public class Bar implements IBar {
    @Override
    public void hello () {
        System.out.println ("hello guice!") ;
    }
}

对应的 Foo 里面可以用 @Inject 标注,

package demo ;

import com.google.inject.Inject ;

public class Foo {
    @Inject
    private IBar bar ;

    public void setBar (IBar b) {
        bar = b ;
    }

    public void enjoy () {
        System.out.println ("on vacation!") ;
        bar.hello () ;
    }
}

为了使用这个“模块”,我们提供一个 Module 对这个接口、实现进行配置,

package demo ;

import com.google.inject.AbstractModule ;

public class Module extends AbstractModule {
    @Override
    protected void configure () {
        bind (IBar.class).to (Bar.class) ;
    }
}

最后是程序入口

package demo;

import com.google.inject.Injector ;
import com.google.inject.Guice ;

public class App {
    public static void main( String[] args ) {
        Injector inj = Guice.createInjector (new Module ()) ;
        Foo f = inj.getInstance (Foo.class) ;
        f.enjoy () ;
    }
}

其实和 spring 总体来说是类似的。下面有些 tips:

  • 如果有同接口的不同实现,某些情况需要选择某一种,可以在代码里面通过自定义的 annotation 标注,即 annotation 映射到具体的实现类上,在 module 里面 bind 的时候可以制定某个 annotation 下使用什么实现
  • 自定义 annotation 需要用 @BindingAnnotation 来 annotate,这样才会被 Guice 使用
  • Guice 也支持使用“名称”标识,类似用字符串来标识,这需要使用 @Named
  • Guice 可以对 instance 进行 injection,这样就可以为某些属性赋值了
  • 在 Module 里面使用 @Provides 的方法可以返回需要的接口的实现实例,很多 singleton 可以考虑在这里初始化
  • 除了以上方法提供实例以外,可以实现 Provider<T> 接口
  • 不声明目标的 binding 可以让 injector 为其准备(通常是实现类,没有接口),同时如果有 annotation 等区分时还是需要 to 自己的。
  • binding 可以对 constructor 做,一般写成 toConstructor
  • bind 实现到接口也可以通过 @ImplementedBy 在接口上标明;类似可以用 @ProvidedBy 标识 Provider<T> 的实现类。
  • 类似 spring 也有 scope 的概念,提供 @Singleton 标识这是个 thread safe 的东西
  • guice 也支持 AOP,但是并不是使用类似 aspectj 的方式,它是在 module 配置时使用 bindInterceptor 将 annotation 与 interceptor 关联起来

一个比较麻烦的事情自然是 spring 提供的 contextual session management,这个 Guice 没有,不过可以通过 Guice 来管理 TransactionManager 然后也就可以用 spring 的 @Transactional 来做事情了,但是似乎 Guice 很多方面还是没做完的,比如 JUnit 的支持,等等。hmm… 看起来的确是很 neat 的一个 package。

——————-
And when Rachel saw that she bare Jacob no children, Rachel envied her sister; and said unto Jacob, Give me children, or else I die.

Advertisements
Guice 小探

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s