1. 싱글톤 패턴
스프링 없는 순수한 DI 컨테이너
@Test
@DisplayName("스프링 없는 순수한 DI 컨테이너")
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);
}
싱글톤 패턴을 적용한 객체 사용
@Test
@DisplayName("싱글톤 패턴을 적용한 객체 사용")
void singletonServiceTest() {
SingletonService singletonService1 = SingletonService.getInstance();
SingletonService singletonService2 = SingletonService.getInstance();
ystem.out.println("singletonService1 = " + singletonService1);
System.out.println("singletonService2 = " + singletonService2);
assertThat(singletonService1).isSameAs(singletonService2);
}
public class SingletonService {
private static final SingletonService instance = new SingletonService();
public static SingletonService getInstance() {
return instance;
}
private SingletonService() {}
}
- 싱글톤 패턴 적용 시 고객의 요청이 올 때마다 객체를 생성하는 것이 아니라 이미 만들어진 객체를 공유해서 효율적으로 사용할 수 있음
- 단점
- 의존관계상 클라이언트가 구체 클래스에 의존함 -> DIP 위반
- 클라이언트가 구체 클래스에 의존해 OCP 원칙을 위반할 가능성이 높음
- 테스트가 어려워짐
- 내부 속성을 변경하거나 초기화하기 어려움
- private 생성자로 자식 클래스를 만들기 어려움
- 결론적으로 유연성이 떨어지고 안티패턴으로 불리기도 함
2. 싱글톤 컨테이너
스프링 컨테이너와 싱글톤
@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);
assertThat(memberService1).isSameAs(memberService2);
}
- 싱글톤 컨테이너 적용 후 고객의 요청이 올 때마다 객체를 생성하는 것이 아닌 이미 만들어진 객체를 공유해서 효율적으로 재사용할 수 있음
- 싱글톤 컨테이너를 이용하면 싱글톤 패턴만 이용했을 때의 단점을 보완할 수 있음
🚫 주의점
- 싱글톤 패턴과 스프링 같은 싱글톤 컨테이너처럼 객체 인스턴스를 하나만 생성해서 공유하는 싱글톤 방식은 여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하기 때문에 상태유지(Stateful)하게 설계하면 안됨
무상태(Stateless)
로 설계해야 함
- 특정 클라이언트에 의존적인 필드가 있으면 안됨
- 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안됨
- 가급적 읽기만 가능해야 함
- 필드 대신 자바에서 공유되지 않는 지역변수, 파라미터, ThreadLocal 등을 사용해야 함
@Configuration
- 스프링 컨테이너는 싱글톤 레지스트리 -> 스프링 빈이 싱글톤이 되도록 보장해주어야 함
CGLIB 라이브러리
는 @Bean이 붙은 메서드마다 이미 스프링 빈이 존재하면 존재하는 빈을 반환하고, 스프링 빈이 없으면 생성해서 스프링 빈으로 등록하고 반환하는 코드를 동적으로 만들어줌
-> 싱글톤 보장
-> Bean만 사용해도 스프링 빈으로 등록되지만, 싱글톤을 보장하지 않기 때문에 @Configuration
을 사용해야 함
자료 출처