250x250
Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 코드트리
- equals()
- 인터페이스
- 테스트코드
- 싱글톤
- HttpServletResponse
- 프록시
- @configuration
- JSON
- DI
- 코딩테스트
- 백준
- 티스토리챌린지
- fielderror
- 참조변수
- 김영한
- html form
- objecterror
- 오버라이딩
- ocp
- 다형성
- 추상클래스
- 오블완
- 스프링
- 코드트리조별과제
- 서블릿
- java
- 의존관계
- 스프링컨테이너
- http 메시지 컨버터
Archives
- Today
- Total
minOS
싱글톤 컨테이너(2) -주의점 본문
728x90
싱글톤 컨테이너
스프링 컨테이너는 싱글톤 패턴의 문제점을 해결해서 , 객체 인스턴스를 싱글톤(1개만 생성)으로 관리한다.
스프링 빈이 바로 싱글톤으로 관리되는 빈이다.
싱글톤 컨테이너
- 스프링 컨테이너는 싱글톤 패턴을 적용하지 않아도, 객체 인스턴스를 싱글톤으로 관리한다.
ㄴ 이전에 설명한 컨테이너 생성 과정을 자세히 보면(https://onepunchprogram.tistory.com/41) 컨테이너는 객체를 하나만 생성하여 관리한다.
- 스프링 컨테이너는 싱글톤 컨테이너 역할을 한다. 이렇게 싱글톤 객체를 생성하고 관리하는 기능을 싱글톤 레지스트리라 한다.
- 스프링 컨테이너의 이런 기능 덕분에 싱글턴 패턴의 모든 단점을 해결하면서 객체를 싱글톤으로 유지할 수 있다.
ㄴ 싱글톤 패턴을 위해 지저분한 코드가 들어가지 않아도 된다.
ㄴ DIP,OCP,테스트,private 생성자로부터 자유롭게 싱글톤을 사용할 수 있다.
@Test @DisplayName("스프링 컨테이너와 싱글톤") void springContainer(){ ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class); MemberService memberService1 = ac.getBean("memberService", MemberService.class); MemberService memberService2 = ac.getBean("memberService", MemberService.class); System.out.println("memberService1 = " + memberService1); System.out.println("memberService2 = " + memberService2); assertThat(memberService1).isSameAs(memberService2);
참조값 같음
테스트 통과
스프링 컨테이너 덕분에 객체를 생성하는 것이 아니라 이미 만들어진 객체를 공유하여 효율적으로 재사용할 수 있다.
참고) 스프링의 기본 빈 등록 방식은 싱글톤이지만, 요청할때마다 새로운 객체를 생성해서 반환하는 기능도 제공한다. 빈 스코프에서 설명하겠다.
싱글톤 방식의 주의점
- 싱글톤 패턴 또는 싱글톤 컨테이너를 사용하든, 객체 인스턴스를 하나만 생성해서 공유하는 싱글톤 방식은 여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하기 때문에 싱글톤 객체는 상태를 유지(stateful)하게 설계하면 안된다.
- 무상태(stateless)로 설계해야한다.
1) 특정 클라이언트에 의존적인 필드가 있으면 안된다.
2) 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안된다.
3) 가급적 읽기만 해야한다.
4)필드 대신에 자바에서 공유되지 않는 , 지역변수, 파라미터,Threadlocal등을 사용해야 한다.
- 스프링 빈의 필드에 공유 값을설정하면 정말 큰 장애가 발생할 수 있다.
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() { 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원 주문 statefulService2.order("UserB", 20000); //ThreadA: 사용자A 주문 금액 조회 int price = statefulService1.getPrice(); System.out.println(price); assertThat(statefulService1.getPrice()).isEqualTo(20000); } static class TestConfig{ @Bean public StatefulService statefulService(){ return new StatefulService(); } }
출력 결과, 테스트 결과
단순한 설명을 위해 실제 쓰레드는 사용하지 않았다.
ThreadA가 사용자A 코드를 호출하고 ThreadB가 사용자B 코드를 호출한다 가정하자
StatefulService의 price 필드는 공유 필드인데 , 특정 클라이언트가 값을 변경하였다.
따라서 사용자 A의 price 가 10000원이 아니라 20000원이 나왔다.
스프링 빈은 무상태(stateless)로 설계해야한다.
수정 코드public class StatefulService { //private int price ; // 상태를 유지하는 필드 public int order (String name,int price){ System.out.println("name = " + name + " price = " + price); return price; } // public int getPrice(){ // return price; // } }
테스트 코드class StatefulServiceTest { @Test void statefulServiceSingleton() { ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class); StatefulService statefulService1 = ac.getBean(StatefulService.class); StatefulService statefulService2 = ac.getBean(StatefulService.class); //ThreadA: 사용자A가 10000원 주문 int userAPrice = statefulService1.order("UserA", 10000); //ThreadB: 사용자B가 20000원 주문 int userBPrice =statefulService2.order("UserB", 20000); //ThreadA: 사용자A 주문 금액 조회 //int price = statefulService1.getPrice(); System.out.println(userAPrice); //assertThat(statefulService1.getPrice()).isEqualTo(20000); } static class TestConfig{ @Bean public StatefulService statefulService(){ return new StatefulService(); } } }
출력결과UserA의 금액이 제대로 나옴
728x90
'TIL > 김영한의 스프링 핵심 원리' 카테고리의 다른 글
싱글톤 컨테이너(4) - @Configuration과 바이트코드 (0) | 2024.03.13 |
---|---|
싱글톤 컨테이너(3) - @Configuration과 싱글톤 (0) | 2024.03.13 |
싱글톤 컨테이너(1) (0) | 2024.03.07 |
스프링 컨테이너와 스프링 빈(4) (0) | 2024.03.06 |
스프링 컨테이너와 스프링 빈(3) (0) | 2024.03.05 |