싱글톤 패턴은 어떤 객체의 인스턴스를 하나만 생성해서 사용하는 디자인 패턴이다. 클라이언트가 동일한 객체를 요청할 때 매번 새로운 인스턴스를 생성해서 전달하면 그만큼 메모리를 사용하게 되는데 싱글톤 패턴을 사용하면 한개의 인스턴스만 사용하므로 메모리를 절약할 수 있다.
싱글톤 패턴을 직접 구현하게 되면 여러가지 단점이 있다.
스프링 컨테이너는 위 단점들 없이 객체들을 싱글턴 패턴으로 관리해주는 싱글톤 컨테이너의 역할을 한다. (스프링 빈들은 기본적으로 싱글톤 패턴이지만 필요할 경우 요청할 때마다 새로운 객체를 생성해서 반환하는 기능도 있다고 한다.)
@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();
}
}
위 코드에서 AppConfig 클래스에서 스프링 빈을 정의하는 코드를 보면
return new MemberServiceImpl(memberRepository());
return new OrderServiceImpl(memberRepository(), discountPolicy());
이 두 라인에서 memberRepository()
, discountPolicy()
메소드를 호출하고 있는데 그러면
@Bean
public MemberRepository memberRepository() {
System.out.println("call AppConfig.memberRepository");
return new MemoryMemberRepository();
}
이 메소드는 세번이나 호출되므로 MemoryMemberRepository()
인스턴스가 3개 생성되는데 그러면 싱글톤 패턴이 아니다.
스프링에서는 @Configuration
어노테이션을 붙이면 바이트코드를 조작하는 CGLIB 기술로 변형시킨 AppConfig
인스턴스를 사용해서 싱글톤 패턴을 보장한다.
(@Configuration
안 붙이면 Bean Definition 자바 코드가 그대로 작동해서 싱글톤을 보장하지 않으므로 스프링 설정이면 그냥 @Configuration
붙이자..)
싱글톤 객체는 상태를 유지(stateful)하게 설계하면 안된다. 싱글톤 패턴에서는 한개의 인스턴스만 사용하기 때문에 한 클라이언트가 공유되는 필드의 값을 변경하면 같은 인스턴스를 사용하는 다른 클라이언트에서 오류가 발생할 수 있다.
스프링 빈도 기본적으로 싱글톤 패턴이므로 가급적이면 무상태(stateless)로 설계하자..!