Intégration de Google Guice dans Ratpack

Ratpack Google Guice Integration

1. Vue d'ensemble

Dans nosarticleprécédents, nous avons montré à quoi ressemble la création d'applications évolutives à l'aide de Ratpack.

Dans ce didacticiel, nous expliquerons plus en détail comment utiliserGoogle Guice avecRatpack comme moteur de gestion des dépendances.

2. Pourquoi Google Guice?

Google Guice est un framework logiciel open source pour la plateformeJava publié parGoogle sous leApache License.

Il s’agit d’un module de gestion des dépendances extrêmement léger et facile à configurer. En outre, il ne permet l’injection de dépendance de niveau constructeur que par souci de convivialité.

Plus de détails surGuice peuvent être trouvéshere.

3. Utiliser Guice avec Ratpack

3.1. Dépendance Maven

Ratpack a un support de première classe pour la dépendanceGuice. Par conséquent, nous n'avons pas à ajouter manuellement de dépendance externe pourGuice;, il est déjà pré-construit avecRatpack. Plus de détails sur le support deRatpackGuice peuvent être trouvéshere.

Par conséquent, nous devons juste ajouter la dépendance suivante du noyauRatpack dans lespom.xml:


    io.ratpack
    ratpack-core
    1.4.5

Vous pouvez vérifier la dernière version surMavenCentral.

3.2. Modules de service de construction

Une fois la configuration deMaventerminée, nous allons créer un service et utiliser à bon escient une simple injection de dépendances dans notre exemple ici.

Créons une interface de service et une classe de service:

public interface DataPumpService {
    String generate();
}

C'est l'interface de service qui fera office d'injecteur. Maintenant, nous devons construire la classe de service qui l'implémentera et définira la méthode de servicegenerate():

public class DataPumpServiceImpl implements DataPumpService {

    @Override
    public String generate() {
        return UUID.randomUUID().toString();
    }

}

Un point important à noter ici est que puisque nous utilisons le moduleRatpack’s Guice,we don’t need to use Guice‘s @ImplementedBy or @Inject annotation to manually inject the service class.

3.3. Gestion de la dépendance

Il existe deux façons de gérer les dépendances avecGoogle Guice.

La première consiste à utiliser lesAbstractModule deGuice et l'autre à utiliser la méthode du mécanismeinstance binding de Guice:

public class DependencyModule extends AbstractModule {

    @Override
    public void configure() {
        bind(DataPumpService.class).to(DataPumpServiceImpl.class)
          .in(Scopes.SINGLETON);
    }

}

Quelques points à noter ici:

  • en étendantAbstractModule, nous remplaçons la méthode par défautconfigure()

  • nous mappons la classeDataPumpServiceImpl avec l'interfaceDataPumpService qui est la couche de service construite précédemment

  • nous avons également injecté la dépendance commeSingleton.

3.4. Intégration avec l'application existante

La configuration de la gestion des dépendances étant prête, intégrons-la maintenant:

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());
            })));
    }
}

Ici, avec lesregistry() - nous avons lié la classeDependencyModule qui étendAbstractModule. Le moduleRatpack’s Guice fera en interne le reste du nécessaire et injectera le service dans l'applicationContext.

Comme il est disponible dans lesapplication-context,, nous pouvons maintenant récupérer l'instance de service à partir de n'importe où dans l'application. Ici, nous avons récupéré l'instanceDataPumpService du contexte actuel et mappé l'URL/randomString avec la méthodegenerate() du service.

En conséquence, chaque fois que l'URL de/randomString est atteinte, elle renverra des fragments de chaîne aléatoires.

3.5. Liaison d'instance d'exécution

Comme dit précédemment, nous allons maintenant utiliser le mécanisme de liaison d'instance de Guice pour faire la gestion des dépendances au moment de l'exécution. C’est presque la même chose que la technique précédente, hormis l’utilisation de la méthodebindInstance() de Guice au lieu deAbstractModule pour injecter la dépendance:

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());
        })));
    }
}

Ici, en utilisantbindInstance(), nous effectuons une liaison d'instance, c'est-à-dire injecter l'interfaceDataPumpService dans la classeDataPumpServiceImpl.

De cette façon, nous pouvons injecter l'instance de service dans lesapplication-context comme nous l'avons fait dans l'exemple précédent.

Bien que nous puissions utiliser l’une des deux techniques de gestion des dépendances, il est toujours préférable d’utiliserAbstractModule car il séparera complètement le module de gestion des dépendances du code de l’application. De cette façon, le code sera beaucoup plus propre et plus facile à gérer dans le futur.

3.6. Reliure d'usine

Il existe également un autre moyen de gestion des dépendances appeléfactory binding. Il n’est pas directement lié à l’implémentation deGuice’s, mais cela peut également fonctionner en parallèle avecGuice.

Une classe d'usine dissocie le client de l'implémentation. Une usine simple utilise des méthodes statiques pour obtenir et définir des implémentations factices pour les interfaces.

Nous pouvons utiliser les classes de service déjà créées pour activer les liaisons d'usine. Nous avons juste besoin de créer une classe d'usine commeDependencyModule (qui étend la classeGuice’s AbstractModule) et de lier les instances via des méthodes statiques:

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. Par conséquent, à la fois, une seule instance de cette interface serait disponible pour cette classe d’usine. Ensuite, nous avons créé des méthodesgetter/setter normales pour définir et récupérer l'instance de service.

Il convient de noter ici que dans la méthodegetter, nous avons effectué une vérification explicite pour nous assurer qu'une seule instance du service est présente ou non; s'il est nul, alors seulement nous avons créé l'instance de la classe de service d'implémentation et renvoyé la même chose.

Ensuite, nous pouvons utiliser cette instance de fabrique dans la chaîne d’application:

.get("factory", ctx -> ctx.render(ServiceFactory.getInstance().generate()))

4. Essai

Nous utiliserons lesMainClassApplicationUnderTest deRatpack pour tester notre application à l'aide du framework de test JUnit interne deRatpack. Nous devons ajouter les dépendances nécessaires (ratpack-test) pour cela.

Il faut noter ici que le contenu de l'URL étant dynamique, nous ne pouvons pas le prédire lors de l'écriture du scénario de test. Par conséquent, nous ferions correspondre la longueur du contenu du point de terminaison URL/randomString dans le cas de test:

@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

Dans cet article rapide, nous avons montré comment utiliserGoogle Guice avecRatpack.

Comme toujours, le code source complet est disponibleover on GitHub.