스프링 빈 클래스는 공유가 될 수 있는 전역 변수를 사용하면 안된다.
스프링 컨테이너는 스프링 빈을 생성할 때 디폴트로 싱글톤 패턴을 사용해 생성합니다.
? singleton(싱글톤) 이란
메모리에 하나의 인스턴스만 존재하도록 생성하는 패턴
따라서 빈을 사용하는 여러 클라이언트가 하나의 인스턴스를 공유하는 결과가 됩니다.
이때, 클라이언트들이 동시에 인스턴스 안 전역변수(상태)에 접근한다면 어떻게 될까요?
상태가 중구난방으로 꼬여버리지 않을까요? -> 그래서 무상태로 설계해야한다는 말입니다.
public class StatefulService {
private int price; // 상태필드
public void order(String name, int price) {
System.out.println(" name = " + name + "\n price = " + price);
this.price = price;
}
public int getPrice() {
return price;
}
} // end class
싱글톤 패턴이 하나의 객체만을 생성하고, 공유하여 사용하기 때문에 여러 클라이언트에서 상태를 변경할 수 있습니다.
그렇기 때문에 특정 클라이언트의 price(상태)를 보장하지 않는데요. 아래 코드에서 보시죠
void statefulServiceSingleton() {
ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
StatefulService statefulService1 = ac.getBean(StatefulService.class);
StatefulService statefulService2 = ac.getBean(StatefulService.class);
//ThreadA: A사용자 10000원 주문
statefulService1.order("userA", 10000);
//ThreadB: B사용자 20000원 주문
statefulService1.order("userB", 20000);
//ThreadA: 사용자A 주문 금액 조회
int price = statefulService1.getPrice();
//ThreadA: 사용자A는 10000원을 기대했지만, 기대와 다르게 20000원 출력
System.out.println("price = " + price);
}
Spring의 권장 방법인 스프링 빈을 무상태로 설계하면 됩니다!
void statefulServiceSingleton() {
ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
StatefulService statefulService1 = ac.getBean(StatefulService.class);
StatefulService statefulService2 = ac.getBean(StatefulService.class);
//ThreadA: A사용자 10000원 주문
statefulService1.order("userA", 10000);
//ThreadB: B사용자 20000원 주문
statefulService1.order("userB", 20000);
//ThreadA: 사용자A 주문 금액 조회
int price = statefulService1.getPrice();
//ThreadA: 사용자A는 10000원을 기대했지만, 기대와 다르게 20000원 출력
System.out.println("price = " + price);
}
static class StatelessService {
public int order(String name, int price) {
System.out.println(" name = " + name + "\n price = " + price);
return price;
}
}
위 코드는 price(상태)를 제거하고 전달받은 price(상태)를 그대로 반환해줌으로써 상태를 관리하는 주체를 클라이언트로 넘기고 있습니다.