La différence entre CDI et EJB Singleton

1. Vue d'ensemble

Dans ce didacticiel, nous examinerons de plus près deux types de singletons disponibles dans Jakarta EE. Nous expliquerons et démontrerons les différences et verrons les usages adaptés à chacun.

Tout d'abord, voyons ce que sont les singletons avant d'entrer dans les détails.

2. Modèle de conception Singleton

Rappelez-vous qu'une manière courante d'implémenter Singleton Pattern consiste à utiliser une instance statique et un constructeur privé:

public final class Singleton { private static final Singleton instance = new Singleton(); private Singleton() {} public static Singleton getInstance() { return instance; } } 

Mais, hélas, ce n'est pas vraiment orienté objet. Et il a quelques problèmes de multi-threading.

Les conteneurs CDI et EJB nous offrent cependant une alternative orientée objet.

3. CDI Singleton

Avec CDI (Contexts and Dependency Injection), nous pouvons facilement créer des singletons en utilisant l' annotation @Singleton . Cette annotation fait partie du package javax.inject . Il demande au conteneur d' instancier le singleton une fois et passe sa référence à d'autres objets lors de l'injection.

Comme nous pouvons le voir, l'implémentation singleton avec CDI est très simple:

@Singleton public class CarServiceSingleton { // ... } 

Notre classe simule un atelier de service automobile. Nous avons beaucoup d'exemples de voitures différentes , mais elles utilisent toutes le même magasin pour l'entretien. Par conséquent, Singleton est un bon choix.

Nous pouvons vérifier qu'il s'agit de la même instance avec un simple test JUnit qui demande deux fois le contexte pour la classe. Notez que nous avons une méthode d'assistance getBean ici pour la lisibilité:

@Test public void givenASingleton_whenGetBeanIsCalledTwice_thenTheSameInstanceIsReturned() { CarServiceSingleton one = getBean(CarServiceSingleton.class); CarServiceSingleton two = getBean(CarServiceSingleton.class); assertTrue(one == two); } 

En raison de l' annotation @Singleton , le conteneur renverra la même référence les deux fois. Si nous essayons cela avec un bean géré simple, cependant, le conteneur fournira une instance différente à chaque fois.

Et bien que cela fonctionne de la même manière pour javax.inject.Singleton ou javax.ejb.Singleton, il existe une différence clé entre ces deux.

4. EJB Singleton

Pour créer un singleton EJB, nous utilisons l' annotation @Singleton du package javax.ejb . De cette façon, nous créons un bean de session Singleton.

Nous pouvons tester cette implémentation de la même manière que nous avons testé l'implémentation CDI dans l'exemple précédent, et le résultat sera le même. Les singletons EJB, comme prévu, fournissent l'instance unique de la classe.

Cependant, les singletons EJB fournissent également des fonctionnalités supplémentaires sous la forme d'un contrôle d'accès concurrentiel géré par conteneur.

Lorsque nous utilisons ce type d'implémentation, le conteneur EJB garantit que chaque méthode publique de la classe est accessible par un seul thread à la fois. Si plusieurs threads tentent d'accéder à la même méthode, un seul thread peut l'utiliser pendant que les autres attendent leur tour.

Nous pouvons vérifier ce comportement avec un simple test. Nous allons introduire une simulation de file d'attente de service pour nos classes singleton:

private static int serviceQueue; public int service(Car car) { serviceQueue++; Thread.sleep(100); car.setServiced(true); serviceQueue--; return serviceQueue; } 

serviceQueue est implémenté comme un entier statique simple qui augmente quand une voiture «entre» dans le service et diminue quand elle «quitte». Si un verrouillage correct est assuré par le conteneur, cette variable doit être égale à zéro avant et après le service, et égale à un pendant le service.

Nous pouvons vérifier ce comportement avec un simple test:

@Test public void whenEjb_thenLockingIsProvided() { for (int i = 0; i < 10; i++) { new Thread(new Runnable() { @Override public void run() { int serviceQueue = carServiceEjbSingleton.service(new Car("Speedster xyz")); assertEquals(0, serviceQueue); } }).start(); } return; } 

Ce test démarre 10 threads parallèles. Chaque thread instancie une voiture et essaie de la réparer. Après le service, il affirme que la valeur de serviceQueue est revenue à zéro.

Si, par exemple, nous exécutons un test similaire sur le singleton CDI, notre test échouera.

5. Conclusion

Dans cet article, nous avons passé en revue deux types d'implémentations de singleton disponibles dans Jakarta EE. Nous avons vu leurs avantages et leurs inconvénients et nous avons également montré comment et quand utiliser chacun d'eux.

Et, comme toujours, le code source complet est disponible à l'adresse over sur GitHub.