[Spring] 스프링 싱글톤

이전에 싱글톤 패턴에 대해 알아보고 사용하는 이유와 문제점을 살펴보았다.

 

싱글톤을 사용하는 이유보다 문제점이 훨씬 많아 안티패턴이라고도 불리는 싱글톤 패턴을 스프링을 이용하면 문제점을 보완하면서 장점을 누릴 수 있다고 한다. 

스프링이 어떻게 싱글톤 패턴을 이용하는지 한번 알아보자.

 

스프링 컨테이너

Spring의 컨테이너는 이전 포스팅에서 언급한 싱글톤 패턴의 문제점을 보완하고, 싱글톤 패턴을 코드로 일일이 구현하지 않고 객체의 인스턴스를 1개만 생성해서 관리한다. 즉, 싱글톤으로 관리할 수 있게 해 준다.

 

Spring은 특정 클래스에 @Bean을 적용하면 스프링 컨테이너는 해당 클래스에 대해 1개의 인스턴스를 만들어서 관리한다. 이 인스턴스는 클라이언트의 요청이 올 때마다 생성되는 것이 아니라 최초에 만들어진 인스턴스를 공유해서 사용한다. (싱글톤 패턴)

 

그러면 이번에도 테스트 코드를 통해 동일한 인스턴스를 공유해서 사용하는지 확인해 보자.

 

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

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

    assertThat(memberService1).isSameAs(memberService2);
}

 

우선 ApplicationContext(스프링 컨테이너)에 빈으로 관리되고 있는 AppConfig라는 설정 파일을 등록한다.

 

여기서 잠깐 ApplicationContext에 대해 알아보고 가자.

Application context

ApplicationContext를 스프링 컨테이너라고 한다. ApplicationContext는 BeanFactory 인터페이스의 하위 인터페이스이다. 즉, ApplicationContext는 BeanFactory에 부가기능을 추가한 것이다.

  • Spring에서는 빈의 생성과 관계설정 같은 제어를 담당하는 IoC(Inversion of Control) 컨테이너인 빈 팩토리(Bean Factory)가 존재한다.
  • 하지만 실제로는 빈의 생성과 관계설정 외에 추가적인 기능이 필요한데, 이러한 이유로 Spring에서는 빈 팩토리를 상속받아 확장한 Application Context를 주로 사용한다.

Bean 요청 시 처리 과정

  1. ApplicationContext는 @Configuration이 붙은 클래스들을 설정 정보로 등록해 두고, @Bean이 붙은 메서드의 이름으로 빈 목록을 생성한다.
  2. 클라이언트가 해당 빈을 요청한다.
  3. Application Context는 자신의 Bean 목록에서 클라이언트가 요청한 이름이 있는지 찾는다.
  4. ApplicationContext는 설정 클래스로부터 빈 생성을 요청하고, 생성된 빈을 돌려준다

 

다시 돌아와 위에서 등록한 AppConfig 클래스는 아래와 같이 구성되어 있다.

@Configuration
public class AppConfig {

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

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

 

MemberService라는 클래스를 @Bean을 적용해 스프링 컨테이너가 관리하도록 설정해 주었다.

 

다시 테스트 코드를 보자

MemberService memberService1 = applicationContext.getBean("memberService", MemberService.class);
MemberService memberService2 = applicationContext.getBean("memberService", MemberService.class);

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

스프링 컨테이너가 관리하고 있는 동일한 memberService를 두 번 불러와 주소값을 출력해 보았다.

 

아래와 같이 memberService의 두 주소값이 동일한 것을 확인할 수 있다. 그 이유는 스프링 컨테이너가 최초에 인스턴스를 1번 생성하고 컨테이너에 관리를 하고 있다 이후 요청이 올 때마다 관리하고 있던 인스턴스를 리턴하는 방식이기 때문이다.

 

 

이렇게 스프링 컨테이너로 싱글톤을 구현하면 직접 싱글톤을 구현할 때 보다 코드가 훨씬 간결해지고, private 생성자도 필요가 없어지고 테스트도 용이해지기 때문에 싱글톤의 문제점을 보완하면서도 메모리 낭비를 방지하는 장점을 누릴 수 있다. 

 

출처 : inflearn - 스프링 핵심 원리(기본 편)