본 포스팅은 학습용으로, 김영한 님의 강의를 바탕으로 복습용도로 작성되었습니다.
선행 게시물
[Spring] 싱글톤 컨테이너
본 포스팅은 학습용으로, 김영한 님의 강의를 바탕으로 복습용도로 작성되었습니다. 웹 애플리케이션과 싱글톤 스프링은 태생이 기업용 온라인 서비스 기술을 지원하기 위해 탄생 대부분의 스
muscleking3426.tistory.com
싱글톤 방식의 주의점
싱글톤 패턴, 스프링의 싱글톤 컨테이너 등 객체 인스턴스를 하나만 생성해서 공유하는 싱글톤 방식은 여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하기 때문에 싱글톤 객체는 상태를 유지하게 설계하면 안 된다.
→ stateless 무상태로 설계해야 한다.
- 특정 클라이언트에 의존적인 필드가 존재하면 안 된다
- 특정 클라이언트가 값을 변경할 수 있는 필드가 존재하면 안 된다
- 가급적 읽기만 가능해야 한다
- 필드 대신에 자바에서 공유되지 않는 지역변수, 파라미터, ThreadLocal 등을 사용해야 함
→ 스프링 빈의 필드에 공유 값을 설정하면 정말 큰 장애가 발생할 수 있다
상태를 유지할 경우 발생하는 문제점
public class StatefullService {
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;
}
}
테스트
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import static org.junit.jupiter.api.Assertions.*;
class StatefullServiceTest {
@Test
void statefulServiceSingleton() {
ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
//쓰레드 A : A 사용자가 10000원 주문
StatefullService statefullService1 = ac.getBean(StatefullService.class);
statefullService1.order("userA" , 10000);
//쓰레드 B : B 사용자가 20000원 주문
StatefullService statefullService2 = ac.getBean(StatefullService.class);
statefullService2.order("userB", 20000);
//쓰레드 A: 사용자A 가 주문 금액 조회
int price = statefullService1.getPrice(); //의도 A 사용자는 10000 주문했기 떄문에 10000원이 나와야한다.
System.out.println("price = " + price); //A가 조회하기 전에 B가 와서 20000을 주문해서 20000원이 나온다.
Assertions.assertThat(statefullService1.getPrice()).isEqualTo(20000);
}
static class TestConfig {
@Bean
public StatefullService statefullService() {
return new StatefullService();
}
}
}
[가정] 스레드 A가 사용자 A 코드를 호출하고, 스레드 B가 코드를 호출한다.
StatefullService의 price 필드는 공유되는 필드
→ 특정 클라이언트가 값을 변경
- 사용자 A의 주문금액은 10,000원인데, 20,000원이라는 결과가 나타남.
@Configuration과 싱글톤
Appconfig.class
import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.discount.RateDiscountPolicy;
import hello.core.member.*;
import hello.core.order.OrderSerivce;
import hello.core.order.OrderServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration //애노테이션 기반의 자바 설정 클래스로 스프링 컨테이너
public class AppConfig {
//02.08
//@Bean memberService -> new MemoryMemberRepository()
//@Bean orderService -> MemoryMemberRepository()
// 이러면 싱글톤이 깨지는게 아닌가..
//Test 를 해보자.
@Bean
public MemberService memberService() {
System.out.println("call AppConfig.memberService");
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
System.out.println("call AppConfig.memberRepository");
return new MemoryMemberRepository(); //나중에 DB리포지토리를 쓴다면 이 부분만 바꾸면 된다.
}
@Bean
public OrderSerivce orderService(){
System.out.println("call AppConfig.orderService");
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
@Bean
public DiscountPolicy discountPolicy() {
//return new FixDiscountPolicy();
//문제1. 할인정책을 정률방식으로바꿔보시오.
return new RateDiscountPolicy();
}
}
- memberSerivce 빈을 만드는 코드와 orderService 빈을 만드는 코드는 모두 memberRepository( )를 호출
의문? 각기 다른 MemoryMemberRepository( )가 생성되면서 싱글톤이 깨지는 것이 아닐까?
결론은 아니다.
이유는 Appconfig class 위에 @Configuration 어노테이션 때문이다.
@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$$EnhancerBySpringCGLIB$$ed2356 b2
- AnnotationConfigApplicationContext의 파라미터로 넘긴 값 AppConfig 도 스프링 빈에 등록된다.
- Appconfig의 빈정보라면 순수한 클래스가 아니라 CGLIB가 붙는 것을 볼 수 있다.
- 스프링이 GGLIB라는 바이트 코드 조작 라이브러리를 이용해 AppConfig 클래스를 상속받은 임의의 다른 클래스를 만들고, 그 다른 클래스를 스프링 빈으로 등록한 것.
즉, 스프링 컨테이너에 빈 객체가 등록되어 있으면, 스프링
컨테이너에서 찾아서 반환하고, 아니면 기존 로직을 호출해서 빈 객체를 생성하고, 스프링 컨테이너에 등록한다.
위와 같은 동작 덕분에 스프링은 싱글톤을 보장한다.
'Spring' 카테고리의 다른 글
[Spring] 빈 스코프, 싱글톤과 프로토타입 (0) | 2023.02.13 |
---|---|
[Spring] 컴포넌트 스캔 (0) | 2023.02.10 |
[Spring] 싱글톤 컨테이너 (0) | 2023.02.08 |
[Spring] 컨테이너에 등록된 빈 조회 (0) | 2023.02.08 |
[Spring] 스프링 컨테이너 생성 (0) | 2023.02.08 |