Test의 core 폴더 아래 Singleton 패키지를 생성하고, SingletonTest 클래스를 생성한다.
<코드>
memberService1
와 memberService2
은 다른 객체임을 알 수 있다.싱글톤 패턴이란 클래스의 인스턴스가 딱 1개만 생성되는 디자인 패턴을 의미한다.
테스트 폴더의 Singleton 패키지 아래 SingletonService 클래스를 생성한다.
<코드>
public class SingletonService {
private static final SingletonService instance = new SingletonService();
public static SingletonService getInstance(){
return instance;
}
...
private static final SingletonService instance = new SingletonService();
: 자바가 실행될 때 자동으로 실행되어 자기 자신의 객체를 생성하여 instance
에 참조를 넣어 둔다.getInstance()
를 통해서만 조회할 수 있다.singletonTest
클래스에 다음을 추가한다.
@Test
@DisplayName("싱글톤 패턴을 적용한 객체 사용")
void singletonServiceTest(){
SingletonService singletonService1 = SingletonService.getInstance();
SingletonService singletonService2 = SingletonService.getInstance();
System.out.println("singletonService1 = " + singletonService1);
System.out.println("singletonService2 = " + singletonService2);
}
assertThat(singletonService1).isSameAs(singletonService2);
를 통해 확인할 수도 있다.하지만 싱글톤 패턴은 코드가 복잡해지고 유연성이 떨어지는 등 여러 문제점이 있기 때문에 스프링 컨테이너가 필요하다.
@Test
@DisplayName("싱글톤 컨테이너와 싱글톤")
void springContainer(){
//AppConfig appConfig = new AppConfig();
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);
}
실행 결과 같은 객체를 사용함을 알 수 있다.
고객의 요청이 올 때 마다 새로운 객체를 생성하는 것이 아니라, 이미 만들어진 객체를 공유하여 효율적으로 재사용한다.
싱글톤 패턴으로 설계를 할 때에는 무상태(stateless)로 설계해야 한다.
test 폴더의 Singleton
패키지 아래 StatefulService 클래스를 생성한다.
<코드>
test 폴더의 Singleton
패키지 아래 StatefulServiceTest 클래스를 생성한다.
<코드>
실행 결과 사용자A의 주문 금액이 20000원이라는 결과가 나왔다. StatefulService
의 price
필드가 공유되는데, 클라이언트가 값을 변경하기 때문이다.
공유필드는 조심하여야 한다.
의문
1. AppConfig에서 memberService()
은 memberRepository()
를 통해 MemoryMemberRepository()
를 호출한다.
2. 그리고 orderService()
또한 memberRepository()
를 통해 MemoryMemberRepository()
를 호출한다.
3. 다른 2개의 MemoryMemberRepository()
가 생성되는 듯 하는데, 싱글톤이 깨지지 않을까?
test폴더의 Singleton 패키지 아래 클래스를 생성한다.
<코드>
실행 결과 같은 인스턴트가 공유되어 사용되는 것을 알 수 있다.
println을 통해 Bean 함수가 1번씩만 호출되었다.
<코드>
configurationDeep
클래스를 추가한다.configurationTest.java
파일 아래 추가한다. @Test
void configurationDeep(){
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
AppConfig bean = ac.getBean(AppConfig.class);
System.out.println("bean = " + bean.getClass());
}
bean = class hello.core.AppConfig$$SpringCGLIB$$0
@Configuration
이 사용된 AppConfig
클래스를 상속받은 임의의 다른 클래스를 만들고, 이를 스프링 빈으로 등록한다.@Configuration
을 사용하지 않아도 동작하지만, 스프링 컨테이너가 관리하지 않아 싱글톤을 보장하지 않기 때문에 바람직하지 않다.본 포스팅은 김영한 강사의 스프링 핵심 원리 강의를 수강하고 요약한 것으로, 해당 강의의 영상 및 강의자료를 참고하였습니다.