카테고리 없음

[SpringBoot] 스프링 컨테이너와 싱글톤 패턴

poppy 2022. 1. 13. 00:07
반응형

이전 포스팅에서 직접 객체를 생성하고 DI를 적용한 것을 스프링으로 전환해보았다.

 

스프링 컨테이너

스프링 컨테이너는 @Configuration이 붙은 AppConfig를 설정 정보로 사용한다. 여기서 @Bean 이라 적힌 메서드를 모두 호출해서 반환된 객체를 스프링 컨테이너에 등록한다. 이렇게 스프링 컨테이너에 등록된 객체를 스프링 빈이라고 한다. 이제부터는 스프링 컨테이너를 통해서 스프링 빈을 찾으면 된다.

@Configuration // 여기에 설정을 구성한다는 의미
public class AppConfig {

    @Bean // 스프링 컨테이너에 스프링 빈으로 등록
    public MemberService memberService(){
        System.out.println("call AppConfig.memberService");
        return new MemberServiceImpl(memberRepository());
    }

    @Bean
    public MemberRepository memberRepository(){
        System.out.println("call AppConfig.memberRepository");
        return new MemoryMemberRepository();
    }

    @Bean
    public OrderService orderService(){
        System.out.println("call AppConfig.orderService");
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }

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

 

싱글톤 패턴

- 클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴

- private 생성자를 사용해서 외부에서 임의로 new 키워드를 사용하지 못하도록 막아서 객체가 2개 이상 생성되지 못하도록 막는다

- 싱글톤 패턴을 적용하면 이미 만들어진 객체를 공유해서 효율적으로 사용할 수 있다

 

하지만, 단점도 있다.

- 싱글톤 패턴을 구현하는 코드 자체가 많이 들어간다

- 의존관계상 클라이언트가 구체 클래스에 의존한다 -> DIP, OCP를 위반할 가능성이 높다

- 테스트하기 어렵다

- 유연성이 떨어진다

 

스프링 컨테이너는 이런 싱글톤 패턴의 문제점을 해결하면서, 객체 인스턴스를 싱글톤으로 관리한다. 스프링 컨테이너는 싱글톤 컨테이너 역할을 한다. 따라서 스프링 컨테이너를 적용하면 싱글톤 패턴을 위한 지저분한 코드가 들어가지 않아도 되고, DIP/OCP/테스트/private 생성자로부터 자유롭다.

 

다음과 같이 스프링 컨테이너에 싱글톤 패턴이 적용된 것을 코드로 확인할 수 있다.

@Test
@DisplayName("스프링 컨테이너와 싱글톤")
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);
    
    // memberService == memberService2
    assertThat(memberService1).isSameAs(memberService2);
}



 

 

반응형