CDIとEJBシングルトンの違い

1. 概要

このチュートリアルでは、Java EEで利用できるlink:/java-singleton[singletons]の2つのタイプを詳しく見ていきます。 違いを説明および実証し、それぞれに適した使用法を確認します。
最初に、詳細に入る前に、シングルトンとは何かを見てみましょう。

2. シングルトン設計パターン

link:/java-singleton[Singleton Pattern]を実装する一般的な方法は、静的インスタンスとプライベートコンストラクターを使用することであることを思い出してください。
public final class Singleton {
    private static final Singleton instance = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return instance;
    }
}
しかし、残念ながら、これは実際にはオブジェクト指向ではありません。 また、いくつかのlink:/java-singleton#enum [マルチスレッドの問題]があります。
*ただし、CDIおよびEJBコンテナはオブジェクト指向の代替手段を提供します。*

3. CDIシングルトン

link:/java-ee-cdi[CDI(Contexts and Dependency Injection)]を使用すると、_ @ Singleton_アノテーションを使用してシングルトンを簡単に作成できます。 この注釈は、_javax.inject_パッケージの一部です。 シングルトンを一度インスタンス化するようにコンテナに指示し、注入中にその参照を他のオブジェクトに渡します。
ご覧のとおり、CDIを使用したシングルトンの実装は非常に簡単です。
@Singleton
public class CarServiceSingleton {
    // ...
}
私たちのクラスは、カーサービスショップをシミュレートします。 さまざまな__Car__sのインスタンスが多数ありますが、それらはすべて同じショップを使用してサービスを提供しています。 したがって、Singletonが適しています。
クラスのコンテキストを2回要求する単純なJUnitテストで、同じインスタンスであることを確認できます。 https://github.com/eugenp/tutorials/blob/a7b58ec018be7e28687869b4ae20cb086e3dd89a/jee-7/src/test/java/com/baeldung/singleton/CarServiceIntegrationTest.java#L70[_getBean_]ヘルパーメソッドはこちらです。読みやすくするために:
@Test
public void givenASingleton_whenGetBeanIsCalledTwice_thenTheSameInstanceIsReturned() {
    CarServiceSingleton one = getBean(CarServiceSingleton.class);
    CarServiceSingleton two = getBean(CarServiceSingleton.class);
    assertTrue(one == two);
}
  • _ @ Singleton_注釈のため、コンテナは両方の場合に同じ参照を返します。*プレーンなマネージドBeanでこれを試みると、コンテナは毎回異なるインスタンスを提供します。

    これは_javax.inject.Singleton_または__javax.ejb.Singletonのどちらでも同じように機能しますが、while __ **これら2つの間に重要な違いがあります**。

4. EJBシングルトン

EJBシングルトンを作成するには、_javax.ejb_パッケージの_ @ Singleton_アノテーションを使用します。 このようにして、https://www.baeldung.com/java-ee-singleton-session-bean [Singleton Session 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. 結論

この記事では、Java EEで利用可能な2種類のシングルトン実装を検証しました。 それらの長所と短所を確認し、それぞれを使用する方法とタイミングを示しました。
そして、いつものように、完全なソースコードはhttps://github.com/eugenp/tutorials/tree/master/jee-7[GitHubで]から入手できます。