스프링 DI 컨테이너(싱글톤 컨테이너)
- 스프링 컨테이너를 통해 사용자의 요청이 올 때 마다 객체를 생성하는 것이 아니라, 이미 만들어진 객체를 공유해서 효율적으로 재사용할 수 있게 됌
싱글톤 방식의 주의점
- 여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하기 때문에 싱글톤 객체는 상태를 유지하게 설계하면 안됌
- 무상태로 설계해야 함
- 특정 클라이언트에 의존적인 필드가 있으면 안됌
- 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안됌
- 가급적 읽기만 가능해야 함
- 필드 대신에 자바에서 공유되지 않는 지역변수, 파라미터 등을 사용해야 함
- 스프링 빈의 필드에 공유 값을 설정하면 정말 큰 장애가 발생할 수 있음
public class StatefulService {
private int price; // 상태를 유지하는 필드
public void order(String name, int price) {
this.price = price
}
public int getPrice() {
return price;
}
}
public class StatefulServiceTest {
@Test
void statefulServiceSingleton() {
ApplicationContext ac = new AnnotationApplicationContext(TestConfig.class);
StatefulService ss1 = ac.getBean("statefulService", StatefulService.class);
StatefulService ss2 = ac.getBean("statefulService", StatefulService.class);
ss1.order("userA", 10000);
ss2.order("userB", 20000);
int price = ss1.getPrice();
// 10000원을 기대했지만 20000원 출력
}
}
- price라는 필드를 공유하고 있기 때문에 상태가 유지되는 필드의 변화가 다른 사용자의 요청에도 영향을 미침
- 스프링 빈은 항상 무상태로 설계해야 함
@Configuration과 바이트코드 조작
AppConfig {
userRepository() {
return new MemoryUserRepository();
}
// 여기서 한번
userService() {
return new UserServiceImpl(userRepository())
}
// 여기서 한번
orderService() {
return new OrderServiceImpl(userRepository(), discountPolicy());
}
}
- 두번이 호출될 것 같지만 한번만 호출된다
- 사실 AnnotationConfigApplicationContext에 파라미터로 넘긴 값은 스프링 빈으로 등록된다
- 그래서 AppConfig도 스프링 빈이 된다
- AppConfig 스프링 빈을 직접 등록하는 것이 아닌 CGLIB라는 바이트코드 조작 라이브러리를 사용해서
- AppConfig를 상속받은 임의의 AppConfig@CGLIB를 만들고 이를 스프링 빈으로 등록한 것이다
// CGLIB 동작 예상
if memoryUserRepository가 이미 스프링 컨테이너에 등록되어 있으면
return 스프링 컨테이너에서 찾아서 반환
else
기존 로직을 호출해서 MemoryUserRepository를 생성하고 스프링 컨테이너에 등록
'Spring > Basic' 카테고리의 다른 글
[Basic] 의존관계 주입 (0) | 2024.08.27 |
---|---|
[Basic] 컴포넌트 스캔 (0) | 2024.08.27 |
[Basic] BeanFactory / ApplicationContext (0) | 2024.08.27 |
[Basic] 의존 관계 설정 관련해서 알면 좋은 개념 (0) | 2024.08.21 |
[Basic] 간단하게 기억할 내용 (0) | 2024.08.21 |