Updated:

1. 개요

스프링 컨테이너는 싱글톤 컨테이너 역할을 하여 싱글턴 패턴의 단점들을 해결하면서 객체를 싱글톤으로 유지할 수 있다. 이번에는 싱글톤 컨테이너(Singleton Container)에 대해 알아보도록 하자.

2. 일반 DI 컨테이너

스프링을 사용하지 않는 경우 일반적으로 요청할 때마다 새로운 객체 인스턴스를 생성하여 반환한다. 따라서 메모리 낭비가 심하다는 단점이 있다.

[AppConfig.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class AppConfig {

    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }

    public OrderService orderService() {
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }

    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }

    public DiscountPolicy discountPolicy() {
        return new RateDiscountPolicy();
    }
}

[SingletonTest.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class SingletonTest {

    @Test
    void pureContainer() {
        AppConfig appConfig = new AppConfig();

        MemberService memberService1 = appConfig.memberService();
        MemberService memberService2 = appConfig.memberService();

        System.out.println("memberService1 = " + memberService1);
        System.out.println("memberService2 = " + memberService2);

        assertThat(memberService1).isNotSameAs(memberService2);
    }

[실행결과]

3. 싱글톤 패턴

싱글톤 패턴이란 객체 인스턴스를 한 개만 생성하여 전역적으로 접근 가능하도록 하는 패턴이다.

3-1. 싱글톤 패턴 예제

[SingletonService.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class SingletonService {

    private static final SingletonService instance = new SingletonService();

    public static SingletonService getInstance() {
        return instance;
    }

    private SingletonService() {}

    public void logic() {
        System.out.println("싱글톤 객체 로직 호출");
    }
}

Line 3 : static 영역에 인스턴스 생성

Line 5 ~ 7 : 인스턴스 조회 시 항상 같은 인스턴스 반환

Line 9 : 생성자는 private으로 정의하여 외부에서 new로 생성이 불가능하도록 막음

[singletonServiceTest.java]

1
2
3
4
5
6
7
8
9
10
@Test
void singletonServiceTest() {
    SingletonService singletonService1 = SingletonService.getInstance();
    SingletonService singletonService2 = SingletonService.getInstance();

    System.out.println("singletonService1 = " + singletonService1);
    System.out.println("singletonService2 = " + singletonService2);

    assertThat(singletonService1).isSameAs(singletonService2);
}

[실행 결과]

3-2. 싱글톤 패턴의 문제점

  • 싱글톤 패턴을 구현하기 위한 코드 필요

  • 구체 클래스에 의존하므로 DIP, OCP 위반

  • private 생성자로 인해 자식클래스 생성이 불가능하여 유연성 떨어짐

4. 싱글톤 컨테이너

싱글톤 컨테이너를 통해 싱글톤 패턴의 문제점들을 해결할 수 있다.

싱글톤은 동일한 객체 인스턴스를 공유하므로 상태를 가지고 있는 경우 다른 객체에 의해 의도치 않게 상태가 변경될 수 있다. 따라서 스프링 빈은 무상태(Stateless)로 설계되어야 한다.

[AppConfig.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Configuration
public class AppConfig {

    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }

    @Bean
    public OrderService orderService() {
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }

    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }

    @Bean
    public DiscountPolicy discountPolicy() {
        return new RateDiscountPolicy();
    }
}

[SingletonTest.java]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class SingletonTest {

    @Test
    void springContainer() {
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
        MemberService memberService1 = ac.getBean("memberService", MemberService.class);
        MemberService memberService2 = ac.getBean("memberService", MemberService.class);

        System.out.println("memberService1 = " + memberService1);
        System.out.println("memberService2 = " + memberService2);

        assertThat(memberService1).isSameAs(memberService2);
    }
}

[실행결과]

Updated:

Leave a comment