使用 Guice 进行依赖注入 - Play 2.4 2.5
Guice 是 Play 的默认依赖注入(进一步的 DI )框架。其他框架也可以使用,但使用 Guice 可以使开发工作更容易,因为 Play 会在面纱下处理事情。
注入 Play API-s
从 Play 2.5 开始,应该使用 DI 创建几个 API-s,这些 API 在早期版本中是静态的。例如,这些是 Configuration , JPAApi , CacheApi 等。
Play API 的注入方法对于类是不同的,该类由 Play 和自定义类自动注入。在自动注入的类中注入就像在字段或构造函数上放置适当的 @Inject 注释一样简单。例如,要在具有属性注入的控制器中注入 Configuration :
@Inject
private Configuration configuration;
或者使用构造函数注入:
private Configuration configuration;
@Inject
public MyController(Configuration configuration) {
this.configuration = configuration;
}
注册 DI 的自定义类中的注入应该像自动注入类一样完成 - 使用 @Inject 注释。
来自自定义类的注入( 不受 DI 限制 )应该通过使用 Play.current()
。injector() 显式调用注入器来完成。例如,要在自定义类中注入 Configuration ,请定义一个配置数据成员,如下所示:
private Configuration configuration = Play.current().injector().instanceOf(Configuration.class);
自定义注入绑定
自定义注入绑定可以使用 @ImplementedBy 注释完成,也可以使用 Guice 模块以编程方式完成。
用 @ImplementedBy 注释注入
使用 @ImplementedBy 注释注入是最简单的方法。下面的示例显示了一个服务,它为缓存提供了一个外观。
-
该服务由接口 CacheProvider 定义如下:
@ImplementedBy(RunTimeCacheProvider.class) public interface CacheProvider { CacheApi getCache(); }
-
该服务由 RunTimeCacheProvider 类实现:
public class RunTimeCacheProvider implements CacheProvider { @Inject private CacheApi appCache; @Override public public CacheApi getCache() { return appCache; } }
注意 : 在创建 RunTimeCacheProvider 实例时会注入 appCache 数据成员。 **
- 缓存检查器被定义为具有 @Inject 批注的控制器的成员,并从控制器调用:
public class HomeController extends Controller {
@Inject
private CacheProvider cacheProvider;
...
public Result getCacheData() {
Object cacheData = cacheProvider.getCache().get("DEMO-KEY");
return ok(String.format("Cache content:%s", cacheData));
}
使用 @ImplementedBy 注释注入会创建固定绑定: 上面示例中的 CacheProvider 始终使用 RunTimeCacheProvider 进行实例化。当存在具有单个实现的接口时,这种方法仅适用于一种情况。对于具有多个实现的接口或者在没有抽象接口的情况下实现为单例的类,它无能为力。老实说,@ ImplementedBy 将在极少数情况下使用。它更可能与 Guice 模块一起使用程序化绑定。
使用默认 Play 模块进行注入绑定
默认的 Play 模块是根项目目录中名为 Module 的类,定义如下:
import com.google.inject.AbstractModule;
public class Module extends AbstractModule {
@Override
protected void configure() {
// bindings are here
}
}
注意 :上面的代码段显示了 configure 中的绑定,但当然任何其他绑定方法都会得到遵守。
对于 CacheProvider 与 RunTimeCacheProvider 的编程绑定 :
- 从 CacheProvider 的定义中删除 @ImplementedBy 注释 : **
public interface CacheProvider {
CacheApi getCache();
}
- 实施模块配置如下:
public class Module extends AbstractModule {
@Override
protected void configure() {
bind(CacheProvider.class).to(RunTimeCacheProvider.class);
}
}
灵活的注入绑定与默认播放模块
RunTimeCacheProvider 在使用假应用程序的 JUnit 测试中不能很好地工作 (请参阅单元测试主题)。因此,单元测试需要不同的 CacheProvider 实现。注入粘合应根据环境进行。
我们来看一个例子吧。
- 类 FakeCache 提供了在运行测试时使用的 CacheApi 的存根实现 (它的实现不是那么有趣 - 它只是一个映射)。
- 类 FakeCacheProvider 实现在运行测试时使用的 CacheProvider :
public class FakeCacheProvider implements CacheProvider {
private final CacheApi fakeCache = new FakeCache();
@Override
public CacheApi getCache() {
return fakeCache;
}
}
- 模块实现如下:
public class Module extends AbstractModule {
private final Environment environment;
public Module(Environment environment, Configuration configuration) {
this.environment = environment;
}
@Override
protected void configure() {
if (environment.isTest() ) {
bind(CacheProvider.class).to(FakeCacheProvider.class);
}
else {
bind(CacheProvider.class).to(RuntimeCacheProvider.class);
}
}
}
这个例子仅用于教育目的。对模块内部的测试进行绑定不是最佳实践,因为这会在应用程序和测试之间进行耦合。测试的绑定应该由测试本身完成,模块不应该知道测试特定的实现。了解如何在…中做得更好。
使用自定义模块进行注入绑定
自定义模块与默认的 Play 模块非常相似。不同之处在于它可能有任何名称,属于任何包装。例如,模块 OnStartupModule 属于包模块。
package modules;
import com.google.inject.AbstractModule;
public class OnStartupModule extends AbstractModule {
@Override
protected void configure() {
...
}
}
应该通过 Play 显式启用自定义模块以进行调用。对于模块 OnStartupModule ,应将以下内容添加到 application.conf 中 :
play.modules.enabled += "modules.OnStartupModule"