여러 클라이언트가 하나의 객체 인스턴스를 공유하기 때문에 싱글톤 객체는 상태를 유지하게 설계하면 안된다.
이는 아주 중요한 개념입니다.
왜 중요할까요 ??
공유값이 바뀌는 에러가 발생
코드로 예를 들어보겠습니다.
[ SingleTon class ]
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;
}
}
[ Test code ]
class StateFulServiceTest {
@Test
void FailSigleTon() {
ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
StateFulService stateFulService1 = ac.getBean("stateFulService", StateFulService.class);
StateFulService stateFulService2 = ac.getBean("stateFulService", StateFulService.class);
stateFulService1.order("parkA", 10000);
stateFulService2.order("parkB", 20000);
int price = stateFulService1.getPrice();
System.out.println("price: " + price);
Assertions.assertThat(stateFulService1.getPrice()).isEqualTo(20000);
}
static class TestConfig{
@Bean
public StateFulService stateFulService() {
return new StateFulService();
}
}
}
위의 코드에서 보면 과정은 이렇습니다.
[ 테스트 코드에서 생성된 스프링 컨테이너는 기본적으로 싱글톤을 사용할 수 있게 설계되어 있습니다. ]
1) parkA라는 주문을 후 getPrice함수로 바로 가격을 출력해줘야하는데
2) 그 중간에 parkB라는 주문이 들어옵니다.
3) 그러면 price 라는 객체를 공유하고 있기 때문에 10000 -> 20000으로 바꿔버리게 됩니다.
4) 그래서 stateFulService1 객체의 getPrice() 함수를 사용해도 20000이 출력되게 되는 오류입니다.
예시의 싱글톤이 이러한 에러가 발생하는 이유는 설계에 아주 큰 문제점이 있습니다.
*** " 테스트 코드 (클라이언트) 가 값을 변경한다 " 라는 것입니다. ***
그래서 스프링 빈은 항상 무상태로 설계해야 하는 것입니다.
그렇다면 위의 예시를 다시 무상태로 설계해보겠습니다.
[ SingleTon class ]
public class StateFulService {
public int order(String name, int _price) {
System.out.println("name:" + name + " price:" + _price);
int price = _price;
return price;
}
}
[ 테스트 코드 ]
class StateFulServiceTest {
@Test
void FailSigleTon() {
ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
StateFulService stateFulService1 = ac.getBean("stateFulService", StateFulService.class);
StateFulService stateFulService2 = ac.getBean("stateFulService", StateFulService.class);
int price1 = stateFulService1.order("parkA", 10000);
int price2 = stateFulService2.order("parkB", 20000);
Assertions.assertThat(price1).isEqualTo(10000);
}
static class TestConfig{
@Bean
public StateFulService stateFulService() {
return new StateFulService();
}
}
}
이렇게 공유된 필드를 사용하는게 아니라 지역변수를 통해서 공유하지 못하게 해결할 수 있습니다.
이 외에 싱글톤의 문제점을 해결하는 방법으로 @Configuration 에노테이션을 사용하는 방법이 있습니다.
이 부분은 검색해보시면 많은 자료를 찾아볼 수 있습니다.