Guice2.0的变化——第一部分 新的特性(上)
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(); } }
上一篇: Java基础——面向对象(内部类)