1. 概述
在我们之前的文章中,我们展示了如何使用Ratpack构建可扩展的应用程序。
在本教程中,我们将进一步讨论如何使用Google Guice和Ratpack作为依赖管理引擎。
2. 为什么选择Google Guice?
Google Guice是Google根据Apache许可证发布的Java平台开源软件框架。
它是一个非常轻量的依赖管理模块,易于配置。此外,为了方便使用,它只允许构造函数级别的依赖注入。
关于Guice的更多详细信息请参见此处。
3. 使用Guice和Ratpack
3.1 Maven依赖
Ratpack对Guice依赖提供一流的支持,因此,我们不必手动为Guice添加任何外部依赖;它已随Ratpack预构建。有关Ratpack的Guice支持的更多详细信息,请参见此处。
因此,我们只需要在pom.xml中添加以下核心Ratpack依赖:
<dependency>
<groupId>io.ratpack</groupId>
<artifactId>ratpack-core</artifactId>
<version>1.4.5</version>
</dependency>
你可以在Maven Central上查看最新版本。
3.2 构建服务模块
完成Maven配置后,我们将构建一个服务,并在此示例中充分利用一些简单的依赖注入。
让我们创建一个服务接口和一个服务类:
public interface DataPumpService {
String generate();
}
这是将充当注入器的服务接口。现在,我们必须构建将实现该接口的服务类,并定义服务方法generate():
public class DataPumpServiceImpl implements DataPumpService {
@Override
public String generate() {
return UUID.randomUUID().toString();
}
}
这里要注意的一点是,由于我们使用的是Ratpack的Guice模块,因此我们不需要使用Guice的@ImplementedBy或@Inject注解来手动注入服务类。
3.3 依赖管理
有两种方法可以使用Google Guice执行依赖管理。
第一种是使用Guice的AbstractModule,另一种是使用Guice的实例绑定机制方法:
public class DependencyModule extends AbstractModule {
@Override
public void configure() {
bind(DataPumpService.class).to(DataPumpServiceImpl.class)
.in(Scopes.SINGLETON);
}
}
这里有几点需要注意:
- 通过扩展AbstractModule,我们覆盖了默认的configure()方法
- 我们将DataPumpServiceImpl类与DataPumpService接口进行映射,该接口是之前构建的服务层
- 以Singleton的方式注入了依赖
3.4 与现有应用程序集成
依赖管理配置已经准备好了,现在让我们来集成它:
public class Application {
public static void main(String[] args) throws Exception {
RatpackServer
.start(server -> server.registry(Guice
.registry(bindings -> bindings.module(DependencyModule.class)))
.handlers(chain -> chain.get("randomString", ctx -> {
DataPumpService dataPumpService = ctx.get(DataPumpService.class);
ctx.render(dataPumpService.generate().length());
})));
}
}
在这里,使用registry()-我们绑定了扩展AbstractModule的DependencyModule类,Ratpack的Guice模块将在内部完成其余需要的操作,并在应用程序Context中注入服务。
由于它在应用程序上下文中可用,我们现在可以从应用程序中的任何位置获取服务实例。在这里,我们从当前上下文中获取了DataPumpService实例,并使用服务的generate()方法映射了/randomString URL。
因此,每当访问/randomString URL时,它都会返回随机字符串片段。
3.5 运行时实例绑定
如前所述,我们现在将使用Guice的实例绑定机制在运行时进行依赖管理。除了使用Guice的bindInstance()方法而不是AbstractModule来注入依赖外,它几乎与以前的技术相同:
public class Application {
public static void main(String[] args) throws Exception {
RatpackServer.start(server -> server
.registry(Guice.registry(bindings -> bindings
.bindInstance(DataPumpService.class, new DataPumpServiceImpl())))
.handlers(chain -> chain.get("randomString", ctx -> {
DataPumpService dataPumpService = ctx.get(DataPumpService.class);
ctx.render(dataPumpService.generate());
})));
}
}
这里,通过使用bindInstance(),我们执行实例绑定,即将DataPumpService接口注入到DataPumpServiceImpl类。
通过这种方式,我们可以将服务实例注入到应用程序上下文中,就像我们在前面的例子中所做的那样。
虽然我们可以使用这两种技术中的任何一种进行依赖管理,但最好使用AbstractModule,因为它将依赖管理模块与应用程序代码完全分开。这样,代码将更加干净,将来也更易于维护。
3.6 工厂绑定
还有一种依赖管理方法,称为工厂绑定。它与Guice的实现没有直接关系,但也可以与Guice并行工作。
工厂类将客户端与实现分离,简单工厂使用静态方法来获取和设置接口的Mock实现。
我们可以使用已经创建的服务类来启用工厂绑定,我们只需要创建一个工厂类,就像DependencyModule(它扩展了Guice的AbstractModule类)一样,并通过静态方法绑定实例:
public class ServiceFactory {
private static DataPumpService instance;
public static void setInstance(DataPumpService dataPumpService) {
instance = dataPumpService;
}
public static DataPumpService getInstance() {
if (instance == null) {
return new DataPumpServiceImpl();
}
return instance;
}
}
在这里,我们在工厂类中静态注入服务接口。因此,一次只能有一个该接口的实例可用于此工厂类。然后,我们创建了常规的Getter/Setter方法来设置和获取服务实例。
这里要注意的是,在Getter方法中我们进行了一次明确的检查以确保只存在服务的单个实例;如果它为空,那么我们只会创建实现服务类的实例并返回相同的实例。
此后,我们可以在应用程序链中使用该工厂实例:
.get("factory", ctx -> ctx.render(ServiceFactory.getInstance().generate()))
4. 测试
我们将使用Ratpack的MainClassApplicationUnderTest来测试我们的应用程序,借助Ratpack的内部JUnit测试框架,我们必须为其添加必要的依赖(ratpack-test)。
这里需要注意的是,由于URL内容是动态的,我们在编写测试用例时无法预测它。因此,我们将在测试用例中匹配/randomString URL端点的内容长度:
@RunWith(JUnit4.class)
public class ApplicationTest {
MainClassApplicationUnderTest appUnderTest
= new MainClassApplicationUnderTest(Application.class);
@Test
public void givenStaticUrl_getDynamicText() {
assertEquals(21, appUnderTest.getHttpClient()
.getText("/randomString").length());
}
@After
public void shutdown() {
appUnderTest.close();
}
}
5. 总结
在这篇简短的文章中,我们展示了如何将Google Guice与Ratpack结合使用。
Post Directory
