Integração com o Ratpack Google Guice
1. Visão geral
2. Por que Google Guice?
Google Guice é uma estrutura de software de código aberto para a plataformaJava lançada porGoogle sobApache License.
É um módulo de gerenciamento de dependência extremamente leve e fácil de configurar. Além disso, ele permite apenas a injeção de dependência no nível do construtor em prol da usabilidade.
Mais detalhes sobreGuice podem ser encontradoshere.
3. Usando Guice com Ratpack
3.1. Dependência do Maven
Ratpack tem suporte de primeira classe para dependênciaGuice. Portanto, não precisamos adicionar manualmente nenhuma dependência externa paraGuice;, ela já vem pré-construída comRatpack. Mais detalhes sobre o suporte deRatpackGuice podem ser encontradoshere.
Portanto, precisamos apenas adicionar a seguinte dependência principalRatpack empom.xml:
io.ratpack
ratpack-core
1.4.5
3.2. Módulos de serviço de construção
Depois de concluir a configuração deMaven, construiremos um serviço e faremos bom uso de algumas injeção de dependência simples em nosso exemplo aqui.
Vamos criar uma interface de serviço e uma classe de serviço:
public interface DataPumpService {
String generate();
}
Essa é a interface de serviço que atuará como o injetor. Agora, temos que construir a classe de serviço que irá implementá-la e definir o método de serviçogenerate():
public class DataPumpServiceImpl implements DataPumpService {
@Override
public String generate() {
return UUID.randomUUID().toString();
}
}
Um ponto importante a notar aqui é que, como estamos usando o móduloRatpack’s Guice,we don’t need to use Guice‘s @ImplementedBy or @Inject annotation to manually inject the service class.
3.3. Gerenciamento de Dependências
Existem duas maneiras de executar o gerenciamento de dependência comGoogle Guice.
O primeiro é usarGuice'sAbstractModulee outro é usar o método de mecanismoinstance binding de Guice:
public class DependencyModule extends AbstractModule {
@Override
public void configure() {
bind(DataPumpService.class).to(DataPumpServiceImpl.class)
.in(Scopes.SINGLETON);
}
}
Alguns pontos a serem observados aqui:
-
ao estenderAbstractModule, estamos substituindo o métodoconfigure() padrão
-
estamos mapeando a classeDataPumpServiceImpl com a interfaceDataPumpService, que é a camada de serviço construída anteriormente
-
também injetamos a dependência da maneiraSingleton.
3.4. Integração com o aplicativo existente
Como a configuração de gerenciamento de dependência está pronta, vamos integrá-la:
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());
})));
}
}
Aqui, comregistry() - limitamos a classeDependencyModule que estendeAbstractModule. O móduloRatpack’s Guice fará internamente o resto do necessário e injetará o serviço no aplicativoContext.
Como está disponível emapplication-context,, agora podemos buscar a instância do serviço de qualquer lugar no aplicativo. Aqui, buscamos a instânciaDataPumpService do contexto atual e mapeamos a URL/randomString com o métodogenerate() do serviço.
Como resultado, sempre que o URL/randomString for atingido, ele retornará fragmentos de string aleatórios.
3.5. Vinculação de instância de tempo de execução
Como dito anteriormente, agora usaremos o mecanismo de ligação de instância de Guice para fazer o gerenciamento de dependência em tempo de execução. É quase igual à técnica anterior, exceto pelo uso do métodobindInstance() de Guice em vez deAbstractModule para injetar a dependência:
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());
})));
}
}
Aqui, usandobindInstance(), estamos realizando associação de instância, ou seja, injetando a interfaceDataPumpService para a classeDataPumpServiceImpl.
Dessa forma, podemos injetar a instância de serviço emapplication-context como fizemos no exemplo anterior.
Embora possamos usar qualquer uma das duas técnicas para gerenciamento de dependência, é sempre melhor usarAbstractModule, uma vez que separará completamente o módulo de gerenciamento de dependência do código do aplicativo. Dessa forma, o código será muito mais limpo e fácil de manter no futuro.
3.6. Encadernação de fábrica
Também há mais uma maneira de gerenciamento de dependência chamadafactory binding. Não está diretamente relacionado à implementação deGuice’s, mas também pode funcionar em paralelo comGuice.
Uma classe de fábrica separa o cliente da implementação. Uma fábrica simples usa métodos estáticos para obter e definir implementações simuladas para interfaces.
Podemos usar classes de serviço já criadas para habilitar ligações de fábrica. Precisamos apenas criar uma classe de fábrica comoDependencyModule (que estende a classeGuice’s AbstractModule) e vincular as instâncias por meio de métodos estáticos:
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. Portanto, por vez, apenas uma instância dessa interface estaria disponível para essa classe de fábrica. Em seguida, criamos métodosgetter/setter normais para definir e buscar a instância de serviço.
O ponto a ser observado aqui é que, no métodogetter, fizemos uma verificação explícita para garantir que apenas uma única instância do serviço esteja presente ou não; se for nulo, então apenas criamos a instância da classe de serviço de implementação e retornamos a mesma.
Posteriormente, podemos usar esta instância de fábrica na cadeia de aplicativos:
.get("factory", ctx -> ctx.render(ServiceFactory.getInstance().generate()))
4. Teste
UsaremosRatpack'sMainClassApplicationUnderTest para testar nosso aplicativo com a ajuda da estrutura de teste JUnit interno deRatpack. Temos que adicionar a dependência necessária (ratpack-test) para isso.
O ponto a ser observado aqui é que, como o conteúdo do URL é dinâmico, não podemos prevê-lo ao escrever o caso de teste. Portanto, corresponderíamos ao comprimento do conteúdo do endpoint de URL/randomString no caso de teste:
@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. Conclusão
Neste artigo rápido, mostramos como usarGoogle Guice comRatpack.
Como sempre, o código-fonte completo está disponívelover on GitHub.