[spring] singleton (스프링 핵심 원리-기본편 from 김영한 강사님)
System.out.println("이 게시물은 김영한님의 스프링 핵심 원리-기본편을 참조하였습니다.");
싱글톤 (singleton)
: SW 디자인 패턴중 하나.
생성자가 여러 차례 호출되도 실제로 생성되는 객체는 하나이고,
최초 생성 이후에 호출된 생성자는 최초의 생성자가 생성한 객체를 리턴.
- 싱글톤의 문제점
- 짧게 말해, 유연성 부족.
- 클라이언트가 구체 클래스에 의존한다. => DIP 위배 => OCP 위배
- 테스트가 어렵다.
- 자식 클래스를 만들기 어렵다.
출처 - 싱글턴 패턴 - 위키백과, 우리 모두의 백과사전 (wikipedia.org)
싱글턴 패턴 - 위키백과, 우리 모두의 백과사전
위키백과, 우리 모두의 백과사전.
ko.wikipedia.org
기존의 패턴
public class SingletonTest {
//("스프링 없는 순수한 DI 컨테이너")
@Test
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);
//memberService1 != memberService2
Assertions.assertThat(memberService1).isNotSameAs(memberService2);
// 현재 객체를 사실상 4개 생성하는것이다.
}
}
- 조회할때마다 객체를 새로 생성한다. ==> 시간과 자원을 많이 소모한다.
싱글톤 패턴
public class SingletonService {
private static final SingletonService instance = new SingletonService(); //자기 자신을 static으로 하나 갖는다.
public static SingletonService getInstance() {
return instance;
}
private SingletonService(){
}
public void logic() {
System.out.println("싱글톤 객체 로직 호출");
}
}
- 클래스에서 static으로 선언한 변수는 유일하게 존재하고, 객체 사이에서 공유된다.
- 생성자를 private로 막아준다.
- getInstance() 메서드를 통해서만 조회할수 있게 한다.
스프링에서의 싱글톤
//"스프링 컨테이너와 싱글톤"
@Test
void springContainer() {
// AppConfig appConfig = new AppConfig();
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
//1. 조회: 호출할 대 마다 객체를 생성
MemberService memberService1 = ac.getBean("memberService", MemberService.class);
//2. 조회: 호출할 대 마다 객체를 생성
MemberService memberService2 = ac.getBean("memberService", MemberService.class);
//참조값이 다른것을 확인
System.out.println("memberService1 = " + memberService1);
System.out.println("memberService2 = " + memberService2);
//memberService1 != memberService2
Assertions.assertThat(memberService1).isSameAs(memberService2);
}
스프링 프레임워크는 싱글톤의 단점을 제거한 상태로 싱글톤을 사용한다.
싱글톤 방식의 주의점
- 무상태로 설계해야 한다.
- 특정 클라이언트에 의존적인 필드가 있으면 안되고,
특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안됨.
그리고, 싱글톤 객체는 상태를 유지하게 설계되면 안된다.
class StatefulServiceTest {
@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("userA", 20000);
//ThreadA : 사용자A 주문 금액 조회
int price = statefulService1.getPrice();
System.out.println("price = " + price);
}
static class TestConfig {
@Bean
public StatefulService statefulService() {
return new StatefulService();
}
}
}

- price가 적절치 않게 나옴
class StatefulServiceTest {
@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("userA", 20000);
//ThreadA : 사용자A 주문 금액 조회
System.out.println("price = " + userAPrice);
}
static class TestConfig {
@Bean
public StatefulService statefulService() {
return new StatefulService();
}
}
}
적절한 price값이 나옴.
[출처]