클라이언트가 요청을 보낼때 마다 객체의 인스턴스가 생성된다면???
tps가 과도하게 높은 요청이 보내진다면?
메모리 낭비가 어마무지하게 심할것!!!
싱글톤 패턴은 이를 방지하기위해 딱 한개의 객체만 생성하고 이를 공유하도록 설계됨.
따라서 객체 인스턴스가 두개 이상 생성되지 않도록 막아야함!
private생성자를 사용해서 외부에서 new를 쓰는것을 봉쇄해야함
간단한 싱글톤패턴 적용예시
다만 이런 식으로 싱글톤패턴을 적용하면 수많은 문제점들이 발생한다.
위 방식은 한 객체인스턴스를 여러 클라이언트가 공유하기 때문에 원치 않은 내부속성 변경이나,
여러 객체지향 설계를 위반할 가능성이 커진다.
싱글톤 컨테이너를 사용하더라도 결국은 객체 인스턴스 하나를 공유하는 설계이다.
위에서도 언급했듯 싱글톤 객체는 절대 상태를 유지해서는 안된다!
즉, 무상태(stateless)로 설계해야한다.
스프링 빈 필드에 공유값을 설정하고 그 값을 변경시키는 형태의 코드는 절대 사용해서는 안된다. 공유필드를 실수로 건드리는 순간 한 객체를 공유하는 싱글톤패턴의 특성상 대참사가 발생하게된다! 이는 멀티스레드 환경에서의 안정적 구동, Tread-Safe 보장에 핵심적인 요소이다.
한 예시를 들어보자
@Bean
public class StatefulService {
private int price; //상태를 유지하는 필드
public void order(String name, int price) {
this.price = price;
}
public int getPrice() {
return price;
}
}
위 같은 경우 외부에서
- order("오곡코코볼바", 1500);
- order("그레놀라바", 2500);
이런 식의 두가지 클라이언트 접근이 있었다 가정하자, 클라이언트는 "오곡코코볼바"의 가격이 1500임을 기대하지만 실제로는 price값은 2500이 반환되게 된다.
이같은 경우처럼 빈 내부에 공유필드 값을 설정하면 여러 접근에 의해 값이 계속해서 바뀔 수 있게된다.
따라서 값을 반드시 변경해야하는 경우 공유필드가 아닌 지역변수, 파라미터등 공유되지 않는것을 적극 사용하자.
@Configuration 은 스프링 컨테이너에 등록된 빈들을 싱글톤 방식으로 관리되게 해준다.
@Configuration이 붙지 않은 설정자 클래스를 AnnotationConfigApplicationContext의 파라미터로 넘기고, 이를 통해 스프링 컨테이너를 구성한다면 싱글톤 방식이 보장되지 않는다.
@Configuration을 설정자 클래스에 붙히게 되면, 스프링은 CGLIB이라는 바이트 조작 라이브러리를 통해 파라미터로 넘겨받은 클래스를 상속받은 임의의 다른 클래스를 만드는데, 이 생성된 클래스가 스프링 빈으로 등록되게 되고 조작된 바이트코드로 인해 순수한 자바코드대로가 아닌 싱글톤 방식의 객체 관리를 하게 된다.
CGLIB에 의해 조작된 설정자 클래스는 @Bean을 탐색하며 스프링 빈을 등록하고 반환하는 코드가 동적으로 만들어지게 된다. 이때 만약 이미 등록된 (이미 생성된 객체 인스턴스) 빈을 만나게 된다면, 이미 존재하는 빈은 반환하여 싱글톤을 보장하게된다.
반면, 설정자 클래스에 @Configuration을 적용하지 않고 @Bean만 적용하면 어떨까?
이러한 경우에서 중복된 메서드를 실행하게 된다면, 가장 처음에는 정상적으로 빈 등록을 실시하지만 이후에는 순수 자바 코드대로 메서드가 호출되며 새로운 인스턴스가 생성되게 된다!