CDIとEJBシングルトンの違い
1. 概要
このチュートリアルでは、JakartaEEで利用可能な2種類のシングルトンを詳しく見ていきます。 違いを説明してデモンストレーションし、それぞれに適した使用法を確認します。
まず、詳細に入る前に、シングルトンとは何かを見てみましょう。
2. シングルトンデザインパターン
シングルトンパターンを実装する一般的な方法は、静的インスタンスとプライベートコンストラクターを使用することです。
public final class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
しかし、残念ながら、これは実際にはオブジェクト指向ではありません。 また、マルチスレッドの問題がいくつかあります。
ただし、CDIおよびEJBコンテナは、オブジェクト指向の代替手段を提供します。
3. CDIシングルトン
CDI(コンテキストと依存性注入)を使用すると、@Singletonアノテーションを使用してシングルトンを簡単に作成できます。 このアノテーションは、javax.injectパッケージの一部です。 コンテナにシングルトンを1回インスタンス化するように指示し、インジェクション中に他のオブジェクトへの参照を渡します。
ご覧のとおり、CDIを使用したシングルトン実装は非常に単純です。
@Singleton
public class CarServiceSingleton {
// ...
}
私たちのクラスは、自動車サービスショップをシミュレートします。 さまざまなCarのインスタンスがたくさんありますが、それらはすべて同じショップを使用してサービスを提供しています。 したがって、シングルトンが適しています。
クラスのコンテキストを2回要求する単純なJUnitテストを使用して、同じインスタンスであることを確認できます。 読みやすくするために、ここにgetBeanヘルパーメソッドがあることに注意してください。
@Test
public void givenASingleton_whenGetBeanIsCalledTwice_thenTheSameInstanceIsReturned() {
CarServiceSingleton one = getBean(CarServiceSingleton.class);
CarServiceSingleton two = getBean(CarServiceSingleton.class);
assertTrue(one == two);
}
これはjavax.inject.Singletonまたはjavax.ejb.Singletonのどちらでも同じように機能しますが、これら2つの間に重要な違いがあります。
4. EJBシングルトン
EJBシングルトンを作成するには、javax.ejbパッケージの@Singletonアノテーションを使用します。 このようにして、シングルトンセッションBeanを作成します。
この実装は、前の例でCDI実装をテストしたのと同じ方法でテストでき、結果は同じになります。 予想どおり、EJBシングルトンはクラスの単一インスタンスを提供します。
ただし、 EJBシングルトンは、コンテナー管理の同時実行制御の形式で追加機能も提供します。
このタイプの実装を使用する場合、EJBコンテナは、クラスのすべてのパブリックメソッドが一度に1つのスレッドによってアクセスされることを保証します。 複数のスレッドが同じメソッドにアクセスしようとすると、1つのスレッドだけがそのメソッドを使用し、他のスレッドは順番を待ちます。
この動作は、簡単なテストで確認できます。 シングルトンクラスのサービスキューシミュレーションを紹介します。
private static int serviceQueue;
public int service(Car car) {
serviceQueue++;
Thread.sleep(100);
car.setServiced(true);
serviceQueue--;
return serviceQueue;
}
serviceQueue は、車がサービスに「入る」と増加し、サービスが「出る」と減少する単純な静的整数として実装されます。 コンテナによって適切なロックが提供されている場合、この変数はサービスの前後でゼロに等しく、サービス中は1に等しくなければなりません。
簡単なテストでその動作を確認できます。
@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シングルトンで同様のテストを実行すると、テストは失敗します。
5. 結論
この記事では、JakartaEEで利用可能な2種類のシングルトン実装について説明しました。 それらの長所と短所を確認し、それぞれをいつどのように使用するかも示しました。
そして、いつものように、完全なソースコードはGitHubで利用できます。