Разликата между CDI и EJB Singleton

1. Общ преглед

В този урок ще разгледаме по-отблизо два вида единични модели, налични в Джакарта EE. Ще обясним и демонстрираме разликите и ще видим използванията, подходящи за всеки един.

Първо, нека видим за какво са синглоните, преди да влезем в подробностите.

2. Модел на единичен дизайн

Спомнете си, че често срещаният начин за реализиране на Singleton Pattern е със статичен екземпляр и частен конструктор:

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

Но, уви, това всъщност не е обектно-ориентирано. И има някои проблеми с много нишки.

CDI и EJB контейнерите обаче ни дават обектно-ориентирана алтернатива.

3. CDI Singleton

С CDI (Contexts and Dependency Injection) можем лесно да създаваме единични, използвайки анотацията @Singleton . Тази анотация е част от пакета javax.inject . Той инструктира контейнера да създаде един екземпляр еднократно и предава референцията му на други обекти по време на инжектирането.

Както виждаме, еднократното внедряване с CDI е много просто:

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

Нашият клас симулира автосервиз. Имаме много екземпляри от различни автомобили , но всички те използват един и същ магазин за обслужване. Следователно, Singleton е подходящ.

Можем да проверим, че това е един и същ екземпляр с прост JUnit тест, който два пъти задава контекста за класа. Имайте предвид, че тук имаме помощен метод getBean за четливост:

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

Поради анотацията @Singleton контейнерът ще връща една и съща препратка и двата пъти. Ако опитаме това с обикновен управляван боб, обаче контейнерът ще предоставя различен екземпляр всеки път.

И докато това работи еднакво за javax.inject.Singleton или javax.ejb.Singleton, има ключова разлика между тези две.

4. EJB Singleton

За да създадете EJB Сингълтън използваме @Singleton анотация от javax.ejb пакета. По този начин ние създаваме Singleton Session Bean.

Можем да тестваме това изпълнение по същия начин, както тествахме CDI изпълнението в предишния пример, и резултатът ще бъде същият. EJB единични, както се очаква, предоставят единичния екземпляр на класа.

Въпреки това, EJB Singletons предлагат и допълнителна функционалност под формата на контролиран от контейнера контрол на паралелността.

Когато използваме този тип имплементация, контейнерът EJB гарантира, че всеки публичен метод от класа е достъпен по една нишка наведнъж. Ако няколко нишки се опитват да получат достъп до един и същ метод, само една нишка може да го използва, докато други чакат своя ред.

Можем да проверим това поведение с прост тест. Ще въведем симулация на опашка от услуги за нашите сингълтон класове:

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

serviceQueue е реализиран като обикновено статично цяло число, което се увеличава, когато автомобил „влезе“ в услугата и намалява, когато „напуска“. Ако контейнерът осигурява правилно заключване, тази променлива трябва да бъде равна на нула преди и след услугата и равна на единица по време на услугата.

Можем да проверим това поведение с прост тест:

@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; } 

Този тест стартира 10 паралелни нишки. Всяка нишка създава екземпляр на автомобил и се опитва да го обслужва. След услугата той твърди, че стойността на serviceQueue е обратно на нула.

Ако например изпълним подобен тест на CDI singleton, нашият тест ще се провали.

5. Заключение

В тази статия преминахме през два типа единични реализации, налични в Джакарта EE. Видяхме техните предимства и недостатъци и също така демонстрирахме как и кога да използваме всеки от тях.

И както винаги, пълният изходен код е достъпен в GitHub.