싱글톤의 문제점 과 해결법
앞서 웹 어플리케이션에서 싱글톤을 왜 사용하는지 알아 봤다.
이제는 싱글톤의 문제점과 해결법을 알아보자.
문제점
1. 싱글톤의 공유변수 사용을 주의 하자.
2. 싱글톤 패턴이든, 스프링 같은 싱글톤 컨테이너를 사용하든, 객체 인스턴스를 하나만 생성해서 공유하는 싱 글톤 방식은 여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하기 때문에 싱글톤 객체는 상태를 유지 (stateful)하게 설계하면 안된다
3. 무상태로 설계 해야된다.
- 특정 클라이언트에 의존적인 필드가 있으면 안된다.
- 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안된다.
- 가급적 읽기만 가능해야 한다.
- 필드 대신에 자바에서 공유되지 않는 변수, 지역변수, 파라미터 ThreadLocal등을 사용해야 한다.
4. 그렇지 않으면 큰 장애 발생.
- 어떻게 장애가 발생할까? 앞서 싱글톤의 개념은 "하나의 어플케이션에서 하나의 객체만 생성한다고 헀다."
- 그럼 공유변수를 사용하게 되면 어떻게 될까?
5. 공유변수 사용시 발생되는 문제점
- 우리가 만든 싱글톤에 price 라는 변수가 있다고 가정하자
- A 사용자가 10000 을 주문하고 price에 그 값을 저장한다.
- B 사용자가 20000 을 주문하고 price에 그 값을 저장한다.
- 문제가 벌써 발생했다 그럼 앞에 저장한 10000은 20000 으로 변경이 된다.
- 망한것이다.
문제 코드
package hello.core.singletone;
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;
}
}
package hello.core.singletone;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
public class StatefulServiceTest {
@Test
void statefulServiceSingleton() {
AnnotationConfigApplicationContext 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();
System.out.println("price = " + price);
Assertions.assertThat(statefulService1.getPrice()).isEqualTo(20000);
}
static class TestConfig {
@Bean
public StatefulService statefulService() {
return new StatefulService();
}
}
}
문제 해결코드
package hello.core.singletone;
public class StatefulService {
//private int price;
public int order(String name, int price)
{
System.out.println("name = " + name + " price = "+price);
//this.price = price;
return price;
}
//public int getPrice() {
// return price;
//}
}
package hello.core.singletone;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
public class StatefulServiceTest {
@Test
void statefulServiceSingleton() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
StatefulService statefulService1 = ac.getBean(StatefulService.class);
StatefulService statefulService2 = ac.getBean(StatefulService.class);
//ThreadA: A 사용자가 10000원 주문
int useAPrice = statefulService1.order("userA", 10000);
//ThreadB: B 사용자가 20000원 주문
int useBPrice = statefulService1.order("userB", 20000);
//ThreadA: 사용자A 주문 금액 조회
//int price = statefulService1.getPrice();
System.out.println("price = " + useAPrice);
//Assertions.assertThat(statefulService1.getPrice()).isEqualTo(20000);
}
static class TestConfig {
@Bean
public StatefulService statefulService() {
return new StatefulService();
}
}
}
변경된 코드를 보면 price값을 저장하거나 공유하는 부분을 삭제하였다.
이런걸 무상태라고 한다.
'Spring > spring 기초 스터디' 카테고리의 다른 글
[Spring] ComponentScan1 - 기초 (0) | 2021.05.08 |
---|---|
[Spring] Configuration과 Singleton (0) | 2021.05.05 |
[Spring] 웹 애플리케이션과 싱글톤1 (0) | 2021.05.05 |
[Spring] BeanFactory 와 ApplicationContext (0) | 2021.05.01 |
[Spring] Spring Bean 값 조회 하기 (0) | 2021.04.28 |
댓글