스프링을 통해 웹 애플리케이션 개발
-> 여러 고객이 사용 -> 요청할 때마다 객체 새로 생성함

문제 : 고객 트래픽 초당 100 나오면 100개 객체 생성 & 소멸
-> 메모리 낭비
해결 : 해당하는 객체는 한 번만 생성하고, 이걸 공유하기.
-> 싱글톤 패턴
- 클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴
- 객체 인스턴스를 2개 이상 생성하지 못하도록 막아야함.
//1. static 영역에 객체를 딱 1개만 생성해둠
private static final SingletonService instance = new SingletonService();
//2. public으로 열어서 객체 인스턴스 필요하면 해당 메서드 통해서만 조회하도록 하용.
public static SingletonService getInstance(){
return instance;
}
//3. 생성자를 private으로 선언하여, 외부에서 new키워드로 객체 생성 못하게 막음
private SingletonService(){}
싱글톤 패턴의 문제점 해결 + 객체 인스턴스를 싱글톤으로 관리.
지금까지 학습한 스프링 빈이 싱글톤으로 관리되는 빈.

stateless
package hello.core.singleton;
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;
}
}
public class StatefulServiceTest {
@Test
void statefulServiceSingleton() {
ApplicationContext ac = new
AnnotationConfigApplicationContext(TestConfig.class);
StatefulService statefulService1 = ac.getBean("statefulService",
StatefulService.class);
StatefulService statefulService2 = ac.getBean("statefulService",
StatefulService.class);
//ThreadA: A사용자 10000원 주문
statefulService1.order("userA", 10000);
//ThreadB: B사용자 20000원 주문
statefulService2.order("userB", 20000);
//ThreadA: 사용자A 주문 금액 조회
int price = statefulService1.getPrice();
//ThreadA: 사용자A는 10000원을 기대했지만, 기대와 다르게 20000원 출력
System.out.println("price = " + price);
Assertions.assertThat(statefulService1.getPrice()).isEqualTo(20000);
}
static class TestConfig {
@Bean
public StatefulService statefulService() {
return new StatefulService();
}
}
싱글톤에서는 각자 호출하더라도 하나의 객체 공유하다보니 결과가 수정되어 문제인듯. 그래서 stateless하게 짜야하는 것.
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
@Bean
public OrderService orderService() {
return new OrderServiceImpl(
memberRepository(),
discountPolicy());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
...
}
mew MemoryMemberRepository() 호출new MemoryMemberRepository() 호출문제 :MemoryMemberRepository 두 개 생성되는 거 아닌가? 싱글톤 깨지지 않나?
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
//1번
System.out.println("call AppConfig.memberService");
return new MemberServiceImpl(memberRepository());
}
@Bean
public OrderService orderService() {
//1번
System.out.println("call AppConfig.orderService");
return new OrderServiceImpl(
memberRepository(),
discountPolicy());
}
@Bean
public MemberRepository memberRepository() {
//2번? 3번?
System.out.println("call AppConfig.memberRepository");
return new MemoryMemberRepository();
}
@Bean
public DiscountPolicy discountPolicy() {
return new RateDiscountPolicy();
}
}
System.out.println("memberService -> memberRepository = " +
memberService.getMemberRepository());
System.out.println("orderService -> memberRepository = " +
orderService.getMemberRepository());
System.out.println("memberRepository = " + memberRepository);
가정 : memberRepository() 세 번 호출되지 않을까?
결과 :

@Test
void configurationDeep() {
ApplicationContext ac = new
AnnotationConfigApplicationContext(AppConfig.class);
//AppConfig도 스프링 빈으로 등록된다.
AppConfig bean = ac.getBean(AppConfig.class);
System.out.println("bean = " + bean.getClass());
//출력: bean = class hello.core.AppConfig$$EnhancerBySpringCGLIB$$bd479d70
}
순수한 클래스라면 class hello.core.AppConfig 출력되었어야함.
-> 스프링이 CGLIB라는 바이트코드 조작 라이브러리 사용하여, AppConfig 클래스 상속받은 또다른 클래스 만든 후, 빈으로 등록한 것.
@Bean
public MemberRepository memberRepository() {
if (memoryMemberRepository가 이미 스프링 컨테이너에 등록되어 있으면?) {
return 스프링 컨테이너에서 찾아서 반환;
} else { //스프링 컨테이너에 없으면
기존 로직을 호출해서 MemoryMemberRepository를 생성하고 스프링 컨테이너에 등록
return 반환
}
}
bean = class hello.core.AppConfig
call AppConfig.memberService
call AppConfig.memberRepository
call AppConfig.orderService
call AppConfig.memberRepository
call AppConfig.memberRepository
memberRepository 세 번 호출됨.memberService -> memberRepository =
hello.core.member.MemoryMemberRepository@6239aba6
orderService -> memberRepository =
hello.core.member.MemoryMemberRepository@3e6104fc
memberRepository = hello.core.member.MemoryMemberRepository@12359a82
@Configuration 사용하자.