🤗 인프런 [스프링 핵심원리-기본편]을 듣고 기록하는 글입니다
클래스의 인스턴스가 딱 1개만 생성되는것을 보장하는 디자인 패턴
싱글톤 패턴은 여러가지 방식으로 만들 수 있으며, 지금 보여주신 방법은 객체를 미리 생성해두는 가장 단순하고 안전한 방법.
여러 클라이언트가 하나의 객체 instance를 공유
stateful하게 설계하면 절대 안되고 stateless로 설계!!
- 특정 client에 의존적인 필드 NO
스프링 bean의 field에 공유값 설정하면 큰 장애가 생길 수 있음!!
1. 스프링을 사용하지 않은 순수 DI - 싱글톤 보장안됨
@Test
@DisplayName("스프링 없는 순수한 DI 컨테이너")
void pureContainer() {
AppConfig appConfig = new AppConfig();
//1. 조회: 호출할 때 마다 객체를 생성
MemberService memberService1 = appConfig.memberService();
//2. 조회: 호출할 때 마다 객체를 생성
MemberService memberService2 = appConfig.memberService();
//참조값이 다른것을 확인하기
System.out.println("memberService1 = " + memberService1);
System.out.println("memberService2 = " + memberService2);
//항상 테스트는 자동화를 해야하기 때문에 위에처럼 print로 확인하면 안됨
Assertions.assertThat(memberService1).isNotSameAs(memberService2);
}
2. 싱글톤 패턴 구성
void singletonServiceTest() {
new SingletonService();
}
@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);
//same == 진짜 객체 인스턴스가 같은지
//equal == java의 equal처럼 값이 같은지
}
3.싱글톤의 문제점 코드
//서비스 코드
public class StatefulService {
private int price; // 상태유지 필드
public void order(String name, int price) {
System.out.println("name = " + name + " price= " + price);
this.price = price; //여기서 문제가 발생할거임.
}
public int getPrice() {
return price;
}
}
//서비스 코드에 대한 테스트
class StatefulServiceTest {
@Test
void statefulServiceSingleton() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
StatefulService statefulService1 = ac.getBean(StatefulService.class);
StatefulService statefulService2 = ac.getBean(StatefulService.class);
//사용자 A가 주문하고 금액 조회하는 사이에 다른 사용자가 주문을 한 상태
//ThreadA: A사용자 1000원 주문
statefulService1.order("userA", 10000);
//ThreadB: B사용자 2000원 주문
statefulService2.order("userB", 20000);
//ThreadA: 사용자A 주문 금액 조회
//서로 다른 service객체 썼음에도 불구하고 조회시에 B로 나옴
int price = statefulService1.getPrice();
System.out.println("price = " + price);
Assertions.assertThat(statefulService1.getPrice()).isEqualTo(20000);
}
//테스트 전용 test config
static class TestConfig {
@Bean
public StatefulService statefulService() {
return new StatefulService();
}
}
}