je suis un peu confus. Quelle est la différence exacte entre javax.inject.Singleton
et javax.ejb.Singleton
?
J'ai trouvé une explication plausible ici :
Par défaut, les beans session
javax.ejb.Singleton
sont transactionnels (section 13.3.7 de la spécification EJB 3.1) et nécessitent l'acquisition d'un verrou exclusif pour chaque appel de méthode métier (sections 4.8.5.4 et 4.8.5.5).En revanche, un
javax.inject.Singleton
n'est pas transactionnel et ne prend pas en charge la simultanéité gérée par le conteneur (la conséquence majeure étant qu'aucun schéma de verrouillage n'est implémenté par le conteneur). [...]Si vous n'avez pas besoin des fonctionnalités EJB, restez sur
@ApplicationScoped
(javax.inject.Singleton
n'est pas défini par CDI et sa sémantique n'est donc pas régie par cette spécification).
Pour éviter toute confusion, j'utilise ce petit test unitaire (le nom du paquet de premier niveau doit être remplacé):
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import org.junit.Test;
public class SingletonTest {
/** requires com.tngtech.archunit:archunit-junit:0.4.0 */
@Test
public void detectWrongSingletonAnnotation() {
final ClassFileImporter importer = new ClassFileImporter();
final JavaClasses classes = importer.importPackages("first_level_package");
noClasses().should().beAnnotatedWith("javax.inject.Singleton")
.as("Please use javax.ejb.Singleton instead of javax.inject.Singleton.")
.check(classes);
}
}
Comme la réponse acceptée ne résout pas mon problème, je poste ma propre réponse. Ce ne sera pas aussi bon qu'un article d'Adam Bien mais sera certainement plus pratique:
Considérons le code suivant:
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
@Singleton
public class DatabaseConnection {
@PostConstruct
public void init() {
System.out.println("init");
}
public ChampionComp getFirstRecord() {
return new ChampionComp("Ashe", "Teemo", "Warwick",
"Blitzcrank", "Syndra", "Anivia", "Brand", "Rammus", "Xin Zhao", "Irelia");
}
}
Et ce service REST:
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Produces;
import javax.ws.rs.Path;
@Path("/champions")
public class ChampionsAPI {
@Inject
private DatabaseConnection db;
@GET
@Produces("text/plain")
public String getClichedMessage() {
ChampionComp comp = db.getFirstRecord();
return comp.toString();
}
}
En utilisant javax.ejb.Singleton
, ce code fonctionne très bien. L’instance DatabaseConnection
est créée une fois et injectée dans le service REST . Toutefois, si vous remplacez ejb
dans l’importation par inject
, vous recevrez un NPE dans la classe ChampionsAPI lors de l’accès au champ db - c’est que votre Singleton n’a pas été créé (pour une raison quelconque). , peut-être parce qu’il faut utiliser des interfaces en utilisant javax.inject.Singleton
?).