김영한님의 스프링 핵심 원리 — 기본편 강의 내용 중 ‘5강. 싱글톤 컨테이너’에 대한 내용을 정리해봤다.
직전 강의 ‘4강. 스프링 컨테이너와 스프링 빈’에서는 스프링 컨테이너에서 빈을 생성하고 가져오는 방법들에 대해서 공부했다. (관련글 : https://velog.io/@ace0390/%EC%8A%A4%ED%94%84%EB%A7%81-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88%EC%99%80-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B9%88)
이번 강의에서는
- 싱글톤 패턴
- 왜 스프링 컨테이너가 주로 싱글톤으로 빈을 관리하는지
- 어떻게 싱글톤 빈을 관리하는지
에 대해 공부한다.
웹 애플리케이션에서 싱글톤이 좋은 이유
다양한 애플리케이션이 있지만 그 중 웹애플리케이션의 특징 중 하나는 여러 종류의 요청을 동시에 처리해야 한다는 것이다.
만약 싱글톤 객체를 쓰지 않는다면 요청 마다 요청에 필요한 객체들을 새로 생성해야 할 것이고 이 객체들은 요청이 끝난 후 gc 의 대상이 될 것이다. 요청이 많아지면 빈번한 gc 로 인해 시스템에 악영향을 끼칠 수 있다.
요청에 필요한 객체들을 미리 싱글톤 객체로 생성해 놓고 사용하면 위의 문제를 해결할 수 있다.
싱글톤 패턴
클래스의 인스턴스가 메모리상에 1개만 생성되는 것을 보장하는 디자인 패턴이다.
static 키워드와 private 생성자를 이용해 애플리케이션이 올라갈때 SingletonService 객체가 method area 에 하나만 생성되게 함으로써, SingletonService 객체는 메모리에 단 하나만 존재하게 된다.
SingletonService 가 필요한 곳에서는 아래와 같은 코드로 SingletonService 객체를 가져와 사용할 수 있다.
SingletonService singletonService = SingletonService.getInstance();
싱글톤 패턴 주의점
싱글톤 패턴은 여러 클라이언트가 하나의 같은 객체 인스턴스를 공유하기 때문에 싱글톤 객체가 상태를 유지하면 안된다.
- 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안된다.
- 읽기만 가능해야 한다.
- 클래스 변수가 아닌 지역변수, ThreadLocal 등을 사용해야 한다.
싱글톤 패턴 단점
싱글톤 패턴은 위와 같은 장점도 있지만 단점도 존재한다.
- 클라이언트가 구체 클래스의 의존한다. => DIP 위반
- 클라이언트가 구체 클래스에 의존하기 때문에 OCP 원칙을 위반할 가능성이 높다.
- 여러 테스트 세트내에서 하나의 인스턴스가 그 상태를 유지하기 때문에 테스트가 어렵다.
- private 생성자 때문에 자식 클래스를 만들기 어렵다.
싱글톤 컨테이너
- 스프링 컨테이너는 싱글턴 패턴을 적용하지 않아도, 객체 인스턴스를 싱글톤으로 관리한다.
- 스프링 컨테이너 덕분에 싱글턴 패턴의 단점들을 해결하면서 객체를 싱글톤으로 유지할 수 있다.
@Configuration 어노테이션과 싱글톤
빈을 정의하고 있는 AppConfig.java 코드를 보자
AppConfig.memberService() 메서드는 new 연산자로 MemberServiceImpl 객체를 생성하고 있다.
위 코드에서는 appConfig.memberService() 를 2번 호출 후 메서드 결과의 인스턴스가 같은 인스턴스 인지 비교하고 있다.
자바 코드만 놓고 본다면 AppConfig.memberService() 에서 MemberSerivceImpl 객체를 매번 생성하므로 해당 테스트는 실패해야할 것처럼 보이나 성공한다.
위에서 설명했듯이 스프링에서 기본적으로 싱글톤으로 빈을 관리하기 때문이다. 하지만 자바 코드상 분명 실패해야 하는 테스트인데 스프링은 어떻게 memberSerivce 를 싱글톤으로 유지하는 것일까?
@Configuration @Bean
appConfig 객체는 우리가 만든 AppConfig 클래스의 객체가 아닌 스프링이 cglib 라이브러리로 만든 프록시 클래스다. (위의 스크린샷에서 appConfig 를 출력한 것을 보자)
프록시 클래스 내에서 @Bean 이 붙은 메서드 마다 이미 빈이 존재하면 존재하는 빈을 반환하고, 빈이 없다면 생성해서 빈으로 등록하고 반환하는 코드가 동적으로 만들어 지는 것이다.
이렇게 스프링 컨테이너를 사용하면 싱글톤 빈을 보장받을 수 있다.