Guice2 有望于本月发布,但是它并不向下兼容 。很容易让人联想到 Python 和 Python3000 的故事。文章分两部分:“新的特性”和“从 Guice1.0 迁徙到 2.0 ” ,先睹为快吧。
第一部分 新的特性
Small Features
l Binder.getProvider 和 AbstractModule.requireBinding 允许 module 使用声明式的方式,允许依赖于别的 Module 。
l Binder.requestInjection 允许 module 为注册的实例在注入创建时 (Injector-creation time) 进行注入。
l Providers.of() 方法永远保证返回相同的实例,这一点对测试来说很管用。
l Scopes.NO_SCOPE 允许显示的设置一个“无作用域”。
l Matcher.inSubpackage 用于匹配所有指定包下以及其子包下的所有 classes 。
l Types 工具类 ( 改用泛型写了 ) 用于创建一个泛型的具体实现。( Guice 的泛型和标注真是用到出神入化的地步了)
l AbstractModule.requireBinding 允许 module 编程方式的实现依赖。
Provider Methods
创建自定义的 Providers 不需要任何 boilerplate( 模板 ) 。在任意的 module 中,用 @Providers 标注来这样定义一个方法:
public class PaymentsModule extends AbstractModule {
public void configure() {
}
@Provides
@Singleton
PaymentService providePaymentService(CustomerDb database) {
DatabasePaymentService result = new DatabasePaymentService();
result.setDatabase(database);
result.setReplicationLevel(5);
return result;
}
}
在该方法 providePaymentService 中的所有参数都会被自动注入。当然,你还可配合使用 scoping annotation ,咱们这里用的就是 @Singleton 。最后方法返回的类型也就是咱们要绑定的类型。如果你期望方法返回时返回在一个 annotation 上,可以使用 @Named 来创建这样一个 annotated 类型。
当我看到这里的时候,我把我的理解说出来和大伙分享分享。通常来说,我们会在 Action 中像这样注入一些 Service :
public class PaymentAction {
public static final String SUCCESS = "success";
@Inject
private PaymentService paymentService;
public String execute() {
paymentService.execute();
return SUCCESS;
}
}
很好,代码简单明了。但是如果这个“ PaymentService ”在使用之前,还需要注入别的东西的话 ( 比如说 DateSource , Constant 设置等 ) ,那么以往的做法就是在 PaymentService 中继续使用 @Inject 。不过,既然叫 Guice2.0 ,总得弄点新花样吧。这样 @Provides 就来了。它允许你的“ PaymentService ”在创建 module 的时候,将所需要的参数注入进去,进行相应初始化 ( 调用 providePaymentService 方法 ) ,最后再返回给你一个你真正想要的 paymentService 对象;如果你返回的是一个 annotation ,那么也没问题:
public class PaymentAction {
public static final String SUCCESS = "success";
@Inject
Named foo;
public String execute() {
System.out.println(foo.getClass().toString() + " " + foo.value());
return SUCCESS;
}
}
相应的 PaymentsModule 中加入:
public class PaymentsModule extends AbstractModule {
@Named("foo")
private String foo;
...
@Provides
@Singleton
Named provideNamed() throws Exception {
Named name = getClass().getDeclaredField("foo").getAnnotation(
Named.class);
return name;
}
}
我想看到这里,大家应该明白了吧。继续 ~~~
Module functionalTestModule = Modules.override(new ProductionModule()).with(newOverridesModule());
顾名思义,允许用新的 module 来复盖原有的 module 。这样的好处在于,你可以开发测试中使用一套 test module ,而在正式的产品中使用另一套 production module 。
这也是 Guice2 新加入的,先将元素绑定在 Set 和 Map 中,然后注入整个 Collection 。如果多个 module 都有同样的 Collection ,那么 Guice 会帮你合并。
public class SnacksModule extends AbstractModule {
protected void configure() {
MapBinder<String, Snack> mapBinder
= MapBinder.newMapBinder(binder(), String.class, Snack.class);
mapBinder.addBinding("twix").toInstance(new Twix());
mapBinder.addBinding("snickers").toProvider(SnickersProvider.class);
mapBinder.addBinding("skittles").to(Skittles.class);
}
}
PrivateModules 用于创建并不需要对外可见的绑定对象。当然,这样会使得封装变得更加简单,还避免了冲突。
作者没有写关于 PrivateModules 的内容,有可能还没有更新完吧。我们就简单看一个例子:
public class FooBarBazModule extends PrivateModule {
protected void configurePrivateBindings() {
bind(Foo.class).to(RealFoo.class);
expose(Foo.class); //expose方法用于向外部暴露内部的绑定Foo
install(new TransactionalBarModule()); //install方法允许将本PrivateModule模块嵌入到TransactionalBarModule模块中去。
expose(Bar.class).annotatedWith(Transactional.class); //expose方法用于向外部暴露内部的绑定Bar,并令其标注为Transactional
bind(SomeImplementationDetail.class);
install(new MoreImplementationDetailsModule());
}
//向外部暴露的方法
@Provides @Exposed
public Baz provideBaz() {
return new SuperBaz();
}
}