스프링 컨테이너는 싱클톤 패턴의 문제점을 해결하면서, 객체 인스턴스를 싱글톤으로 관리한다. 이러한 스프링 컨테이너의 기능 덕분에 고객의 요청이 올 때 마다 객체를 생성하는 것이 아니라, 이미 만들어진 객체를 공유해서 효율적으로 사용가능하다.
싱글톤 방식은 여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하기 때문에 싱글톤 객체는 무상태로 설계해야 한다.
- 특정 클라이언트에 의존적인 필드가 있으면 안된다.
- 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안된다.
- 필드 대신에 자바에서 공유되지 않는 지역변수, 파라미터 등을 사용해야 한다.
@Test void test(){ ApplicationContext ac = new AnnotationApplicationContext(Stateful.class); Stateful s1 = ac.getBean(Stateful.class); Stateful s2 = ac.getBean(Stateful.class); s1.order("A",1000); s2.order("B",2000); // price = ??? } static class Stateful { private int price; // 상태를 유지하는 필드 public coid order(String name, int price){ this.price = price; } }
ApplicationContext ac =
new AnnotationApplicationContext(AppConfig.class);
Annotation~ 에 파라미터로 넘긴 값은 스프링 빈으로 등록된다. 그렇기 때문에 @Configuration을 적용한 DI 컨테이너도 스프링 빈이 된다. 그럼 스프링이 CGLIB 라는 라이브러리를 사용하여 DI 컨테이너를 상속받은 임의의 다른 클래스를 만들고, 그 다른 클래스를 스프링 빈으로 등록한다. 그 클래스가 싱글톤이 보장되도록 해준다.
// AppConfig@CGLIB 예상 코드
if (빈이 이미 스프링 컨테이너에 등록되어 있다면){
return 스프링 컨테이너에서 찾아서 반환;
} else {
기존 로직을 호출해서 빈을 생성하고 스프링 컨테이너에 등록
return 반환;
}
위와 같이 @Bean이 붙은 메소드마다 이미 스프링 빈이 존재하면 존재하는 빈을 반환하고, 스프링 빈이 없으면 생성해서 등록하고 반환하는 코드가 동적으로 만들어진다. @Configuration 이 CGLIB 기술을 사용해서 싱글톤을 보장하기 때문에 @Bean 만 적용하면 싱글톤을 보장하지 못하고 각각 다른 참조값을 가지게 된다. 그러므로 스프링 설정 정보는 항상 @Configuration 을 사용하자