1. 개요

이 사용방법(예제)에서는 Jakarta EE에서 사용할 수 있는 두 가지 유형의 싱글톤 을 자세히 살펴보겠습니다 . 차이점을 설명하고 시연하고 각각에 적합한 사용법을 확인합니다.

먼저 세부 사항을 살펴보기 전에 싱글톤이 무엇인지 살펴보겠습니다.

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(Contexts and Dependency Injection) 를 사용 하면 @Singleton 어노테이션 을 사용하여 싱글톤을 쉽게 만들 수 있습니다 . 이 어노테이션은 javax.inject 패키지의 일부입니다. 싱글톤을 한 번 인스턴스화 하도록 컨테이너에 지시하고 주입 중에 다른 개체에 대한 참조를 전달합니다.

보시다시피 CDI를 사용한 싱글톤 구현은 매우 간단합니다.

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

우리 수업은 자동차 정비소를 시뮬레이션합니다. 다양한 Car 의 인스턴스가 많이 있지만 서비스를 위해 모두 동일한 상점을 사용합니다. 따라서 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 싱글톤

EJB 싱글톤을 생성하기 위해 javax.ejb 패키지 의 @Singleton 어노테이션을 사용합니다. 이 방법으로 우리는 Singleton Session Bean 을 생성합니다 .

이전 예제에서 CDI 구현을 테스트한 것과 동일한 방식으로 이 구현을 테스트할 수 있으며 결과는 동일합니다. 예상대로 EJB 싱글톤은 클래스의 단일 인스턴스를 제공합니다.

그러나 EJB 싱글톤은 컨테이너 관리 동시성 제어의 형태로 추가 기능도 제공합니다.

이러한 유형의 구현을 사용할 때 EJB 컨테이너는 클래스의 모든 공개 메서드가 한 번에 단일 스레드에 의해 액세스되도록 합니다. 여러 스레드가 동일한 메서드에 액세스하려고 하면 한 스레드만 해당 메서드를 사용하고 다른 스레드는 차례를 기다립니다.

간단한 테스트를 통해 이 동작을 확인할 수 있습니다. 싱글톤 클래스에 대한 서비스 큐 시뮬레이션을 소개합니다.

private static int serviceQueue;

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

serviceQueue  는 자동차가 서비스에 "들어갈" 때 증가하고 "떠날" 때 감소하는 일반 정적 정수로 구현됩니다. 컨테이너에서 적절한 잠금을 제공하는 경우 이 변수는 서비스 전후에 0이고 서비스 중에는 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 의 값 이 다시 0으로 돌아갔다고 주장합니다.

예를 들어 CDI 싱글톤에서 유사한 테스트를 실행하면 테스트가 실패합니다.

5. 결론

이 기사에서는 Jakarta EE에서 사용할 수 있는 두 가지 유형의 싱글톤 구현을 살펴보았습니다. 우리는 그들의 장단점을 보았고 각각을 언제 어떻게 사용하는지 시연했습니다.

그리고 항상 그렇듯이 전체 소스 코드는 GitHub에서 사용할 수 있습니다 .

Generic footer banner