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을 사용하지 않아도 동작하지만, 스프링 컨테이너가 관리하지 않아 싱글톤을 보장하지 않기 때문에 바람직하지 않다.본 포스팅은 김영한 강사의 스프링 핵심 원리 강의를 수강하고 요약한 것으로, 해당 강의의 영상 및 강의자료를 참고하였습니다.