1. Overview
1.概述
In our earlier article, we’ve shown how building scalable applications using Ratpack looks like.
在我们之前的文章中,我们已经展示了使用Ratpack构建可扩展应用程序的情况。
In this tutorial, we will discuss further on how to use Google Guice with Ratpack as dependency management engine.
在本教程中,我们将进一步讨论如何使用Google Guice与Ratpack作为依赖性管理引擎。
2. Why Google Guice?
2.为什么是Google Guice?
Google Guice is an open source software framework for the Java platform released by Google under the Apache License.
Google Guice是一个用于Java平台的开源软件框架,由Google根据Apache许可发布。
It’s extremely lightweight dependency management module that is easy to configure. Moreover, it only allows constructor level dependency injection for the sake of usability.
它是非常轻量级的依赖管理模块,易于配置。此外,为了方便使用,它只允许构造器级别的依赖注入。
More details about Guice can be found here.
关于Guice的更多细节,可以在这里找到。
3. Using Guice With Ratpack
3.使用Guice与Ratpack
3.1. Maven Dependency
3.1.Maven的依赖性
Ratpack has first-class support for Guice dependency. Therefore, we don’t have to manually add any external dependency for Guice; it already comes pre-built with Ratpack. More details on Ratpack‘s Guice support can be found here.
Ratpack对Guice依赖性有一流的支持。因此,我们不需要为Guice手动添加任何外部依赖;它已经预置在Ratpack中了。关于Ratpack的Guice支持的更多细节可以在这里找到。
Hence, we just need to add following core Ratpack dependency in the pom.xml:
因此,我们只需要在pom.xml中添加以下核心Ratpack依赖项。
<dependency>
<groupId>io.ratpack</groupId>
<artifactId>ratpack-core</artifactId>
<version>1.4.5</version>
</dependency>
You can check the latest version on Maven Central.
3.2. Building Service Modules
3.2.构建服务模块
Once done with the Maven configuration, we’ll build a service and make good use of some simple dependency injection in our example here.
完成Maven配置后,我们将建立一个服务,并在这里的例子中好好利用一些简单的依赖注入。
Let’s create one service interface and one service class:
让我们创建一个服务接口和一个服务类。
public interface DataPumpService {
String generate();
}
This is the service interface which will act as the injector. Now, we have to build the service class which will implement it and will define the service method generate():
这是一个服务接口,它将作为注入器发挥作用。现在,我们必须建立一个服务类,实现它,并定义服务方法generate():。
public class DataPumpServiceImpl implements DataPumpService {
@Override
public String generate() {
return UUID.randomUUID().toString();
}
}
An important point to note here is that since we are using Ratpack’s Guice module, we don’t need to use Guice‘s @ImplementedBy or @Inject annotation to manually inject the service class.
这里需要注意的一点是,由于我们使用了Ratpack的Guice模块,我们不需要使用Guice的@ImplementedBy或@Inject注释来手动注入服务类。
3.3. Dependency Management
3.3.依赖性管理
There are two ways to perform dependency management with Google Guice.
有两种方法可以用Google Guice执行依赖性管理。
The first one is to use Guice‘s AbstractModule and other on is to use Guice’s instance binding mechanism method:
第一个是使用Guice的AbstractModule,另一个是使用Guice的instance binding机制方法。
public class DependencyModule extends AbstractModule {
@Override
public void configure() {
bind(DataPumpService.class).to(DataPumpServiceImpl.class)
.in(Scopes.SINGLETON);
}
}
A few of points to note here:
这里有几点需要注意。
- by extending AbstractModule we are overriding default configure() method
- we are mapping DataPumpServiceImpl class with the DataPumpService interface which is the service layer built earlier
- we have also injected the dependency as Singleton manner.
3.4. Integration With the Existing Application
3.4.与现有应用的整合
Since the dependency management configuration is ready, let’s now integrate it:
由于依赖性管理配置已经准备好了,现在让我们来整合它。
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());
})));
}
}
Here, with the registry() – we’ve bound the DependencyModule class which extends AbstractModule. Ratpack’s Guice module will internally do the rest of the needful and inject the service in the application Context.
在这里,通过registry()–我们已经绑定了DependencyModule类,它扩展了AbstractModule。Ratpack的Guice模块将在内部完成其余的工作,并将服务注入到应用程序Context中。
Since it’s available in the application-context, we can now fetch the service instance from anywhere in the application. Here, we have fetched the DataPumpService instance from the current context and mapped the /randomString URL with the service’s generate() method.
由于它在应用-上下文中可用,我们现在可以从应用程序的任何地方获取服务实例。在这里,我们从当前上下文中获取了DataPumpService实例,并通过该服务的generate()方法映射了/randomString URL。
As a result, whenever the /randomString URL is hit, it will return random string fragments.
其结果是,只要点击/randomString URL,它就会返回随机字符串片段。
3.5. Runtime Instance Binding
3.5.运行时实例的绑定
As said earlier, we will now use Guice’s instance binding mechanism to do the dependency management at runtime. It’s almost the same like previous technique apart from using Guice’s bindInstance() method instead of AbstractModule to inject the dependency:
如前所述,我们现在将使用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());
})));
}
}
Here, by using bindInstance(), we’re performing instance binding, i.e. injecting DataPumpService interface to DataPumpServiceImpl class.
在这里,通过使用bindInstance(),我们正在执行实例绑定,即把DataPumpService接口注入到DataPumpServiceImpl类。
In this way, we can inject the service instance to the application-context as we did in the earlier example.
通过这种方式,我们可以像前面的例子那样,将服务实例注入到应用上下文中。
Although we can use any of the two techniques for dependency management, it’s always better to use AbstractModule since it’ll completely separate the dependency management module from the application code. This way the code will be a lot cleaner and easier to maintain in the future.
尽管我们可以使用这两种技术中的任何一种进行依赖管理,但最好还是使用AbstractModule,因为它将把依赖管理模块与应用程序代码完全分开。这样一来,代码就会干净很多,将来也更容易维护。
3.6. Factory Binding
3.6.工厂绑定
There’s also one more way for dependency management called factory binding. It’s not directly related to Guice’s implementation but this can work in parallel with Guice as well.
还有一种依赖性管理的方式,叫做factory binding。它与Guice的实现没有直接关系,但它也可以与Guice并行工作。
A factory class decouples the client from the implementation. A simple factory uses static methods to get and set mock implementations for interfaces.
工厂类将客户端与实现解耦。一个简单的工厂使用静态方法来获取和设置接口的模拟实现。
We can use already created service classes to enable factory bindings. We just need to create one factory class just like DependencyModule (which extends Guice’s AbstractModule class) and bind the instances via static methods:
我们可以使用已经创建的服务类来实现工厂绑定。我们只需要创建一个工厂类,就像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;
}
}
Here, we’re statically injecting the service interface in the factory class. Hence, at a time only one instance of that interface would be available to this factory class. Then, we have created normal getter/setter methods to set and fetch the service instance.
在这里,我们在工厂类中静态地注入了服务接口。因此,在同一时间,只有一个接口的实例可供这个工厂类使用。然后,我们创建了正常的getter/setter方法来设置和获取服务实例。
Point to note here is that in the getter method we have made one explicit check to make sure only a single instance of the service is present or not; if it’s null then only we have created the instance of the implementational service class and returned the same.
这里需要注意的是,在getter方法中,我们做了一个明确的检查,以确保只有一个服务实例存在或不存在;如果它是空的,那么只有我们创建了实现服务类的实例并返回相同的实例。
Thereafter, we can use this factory instance in the application chain:
此后,我们可以在应用链中使用这个工厂实例。
.get("factory", ctx -> ctx.render(ServiceFactory.getInstance().generate()))
4. Testing
4.测试
We will use Ratpack‘s MainClassApplicationUnderTest to test our application with the help of Ratpack‘s internal JUnit testing framework. We have to add the necessary dependency (ratpack-test) for it.
我们将使用Ratpack的MainClassApplicationUnderTest,在Ratpack内部JUnit测试框架的帮助下测试我们的应用程序。我们必须为它添加必要的依赖关系(ratpack-test)。
Point to note here is since the URL content is dynamic, we can’t predict it while writing the test case. Hence, we would match the content length of the /randomString URL endpoint in the test case:
这里需要注意的是,由于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. Conclusion
5.结论
In this quick article, we’ve shown how to use Google Guice with Ratpack.
在这篇快速文章中,我们展示了如何使用Google Guice与Ratpack。
As always, the full source code is available over on GitHub.
一如既往,完整的源代码可在GitHub上获得,。