@Test
@DisplayName("스프링 없는 순수한 DI 컨테이너")
void pureContainer(){
AppConfig appConfig = new AppConfig();
//1.조회 : 호출할 때 마다 객체 생성
MemberService memberService1 = appConfig.memberService();
//2.조회 : 호출할 때 마다 객체를 생성
MemberService memberService2 = appConfig.memberService();
//참조값이 다른 것 확인
System.out.println("memberService1 = " + memberService1);
System.out.println("memberService2 = " + memberService2);
Assertions.assertThat(memberService1).isNotSameAs(memberService2);
}
클래스의 인스턴스가 딱 1개 생성되고, 공유하도록 설계 ➡ 싱글톤 패턴
private 생성자를 사용해서 외부에서 임의로 new키워드를 사용하지 못하도록 막는다.
객체를 생성하려면 메모리 영역을 할당 받아야하는데 한번의 new로 객체 생성을 하면 메모리 낭비를 방지한다.
public class SingletonService {
private static final SingletonService instance = new SingletonService();
public static SingletonService getInstance() {
return instance;
}
private SingletonService() { // private을 통해 외부에서 new 키워드를 사용하지 못하게 막음
}
public void logic() {
System.out.println("싱글톤 객체 로직 호출");
}
}
스프링 컨테이너가 기본적으로 객체를 다 싱글톤으로 만들어서 관리해준다.
무상태로 설계하자!
스프링 빈의 필드에 공유값을 설정하면 정말 큰 장애가 발생할 수 있다.
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;
}
}
@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);
int price = statefulService1.getPrice();
System.out.println("price = " + price);
}
-> A 사용자의 가격 10000이 나올 것을 예상했지만 20000이 나온다.
이 문제를 해결하려면 아래 코드처럼 변경해야한다.
public class StatefulService {
// private int price; 제거
public int order(String name, int price) {
System.out.println("name = " + name + "price = " + price);
return price // return 하도록 수정
}
//public int getPrice() {
// return price;
//}
}
@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);
// int price = statefulService1.getPrice();
System.out.println("price = " + userAprice);
}