빙응의 공부 블로그
[Spring]스프링 핵심 원리 - 싱글톤 컨테이너 본문

📝웹 애플리케이션과 싱글톤
- 스프링은 태생이 기업용 온라인 서비스 기술을 지원하기 위해 탄생했다.
- 대부분의 스프링 애플리케이션은 웹 애플리케이션이다. 물론 웹이 아닌 애플리케이션 개발도 가능하다.
- 그리고 웹 애플리케이션 특성한 다중 처리가 필수이다.

- 기본적인 컨테이너는 서비스 요청마다 MemberServie 객체를 생성해야 된다.
- 그러나 이렇게 되면 사용자가 많을 시 수많은 객체가 생성되어 메모리 낭비가 심해진다.
- 그래서 스프링 컨테이너들은 1개의 객체만을 생성하는 싱글톤 패턴을 사용한다.
📝싱글톤 패턴 (추후 따로 포스팅할 예정)
- 클래스의 인스턴스가 딱 1개만 생성되는 것을 보장하는 디자인 패턴이다.
- 그래서 객체 인스턴스를 2개 이상 생성하지 못하도록 막는 것이다.
package com.example.basics.basics.Singleton;
public class SingletonServie {
//static final로 선언하여 불변하면서 1개만 존재하는 객체를 만든다.
private static final SingletonServie instance = new SingletonServie();
public static SingletonServie getInstance() {
return instance;
}
//생성자를 private으로 선언해서 외부에서 new 키워드를 사용한 객체 생성을 못하게 막는다.
private SingletonServie() {
}
public void logic() {
System.out.println("싱글톤 객체 로직 호출");
}
}
싱글톤 패턴의 문제점
- 구현에 많은 코드가 들어간다.
- 의존 관계 상 클라이언트가 구체 클래스에 의존한다. -> DIP 위반
- 클라이언트가 구체 클래스에 의존하여 OCP 원칙을 위반할 수 있다.
- 테스트가 어렵다.
- 내부 속성을 변경하거나 초기화가 어렵다
- private 생성자로 인해 자식 클래스를 만들기 어렵다.
- 유연성이 떨어짐
📝싱글톤 컨테이너
- 스프링 컨테이너는 싱글톤 패턴의 문제를 해결하면서, 객체 인스턴스를 싱글톤으로 관리한다.
- 지금까지 공부한 스프링 빈이 바로 싱글톤으로 관리되는 빈이다.

해당 사진처럼 고객이 서비스를 요청해 올때마다 객체를 생성하지 않고
이미 만들어진 1개의 객체를 이용하여 서비스를 제공한다.
싱글톤 방식의 주의점
- 객체 인스턴스를 1개를 사용하기 때문에 특별한 주의점이 있다.
- 무상태로 설계해야한다.
- 특정 클라이언트에 의존적인 필드가 있으면 안된다.
- 특정 클라이언트가 값을 변경할 수 잇는 필드가 있으면 안된다.
- 가급적 읽기만 가능해야 한다.
- 필드 대신 자바에서 공유되지 않은, 지역변수, 파라미터, 등을 사용한다.
- 스프링 빈의 필드에 공유 값을 설정하면 정말 큰 장애가 발생할 수 있다.
@Component
public class MySingletonBean {
//상태 값
private int counter = 0;
public void increaseCounter() {
this.counter++;
}
public int getCounter() {
return this.counter;
}
}
위 코드를 보면 평범한 코드인데 스레드를 사용한다면 의미가 달라진다. 상태 값에 의해 사용자가 결정되는데
싱글톤은 1개만 생성되기 때문에 사용자들이 모두 상태 값을 공유하여 Data less(데이터 변질)이 발생할 수 있다.
쉽게 생각하면 스레드 동기화 문제라 보면 된다.
항상 공유필드에 대해 조심하자!!!
📝@Configuration과 싱글톤
@Configuration
public class AppConfig {
@Bean
public MemberService memberService(){
return new MemberServiceImpl(memberRepository());
}
@Bean
public OrderService orderService(){
return new OrderServiceImpl(memberRepository(),discountPolicy());
}
/**
*MemberRepository 구현
*
*/
@Bean
public MemberRepository memberRepository(){
return new MemoryMemberRepository();
}
/**
*DiscountPolicy 구현
*
*/
@Bean
public DiscountPolicy discountPolicy(){
return new RateDiscountPolicy();
}
}
스프링 컨테이너는 싱글톤을 보장한다. 그러나 위 코드를 보자
- memberSerivce -> new memberRepository
- orderService -> new memberRepository
위 두개만 봐도 두번의 객체가 생성된다. 싱글톤이 깨지는 것이다.
@Configuration과 바이트코드 조작
스프링 컨테이너는 싱글톤을 보장한다. 따라서 스프링 빈이 싱글톤이 되도록 보장해준다. 그런데 스프링이 자바 코드까지 어떻게 하기는 어렵기에 위의 코드를 보면 2번 호출되는 것이 맞다.
그러나 스프링은 클래스의 바이트코드를 조작하는 라이브러리를 사용한다.
@Test
void configurationDeep() {
ApplicationContext ac = new
AnnotationConfigApplicationContext(AppConfig.class);
//AppConfig도 스프링 빈으로 등록된다.
AppConfig bean = ac.getBean(AppConfig.class);
System.out.println("bean = " + bean.getClass());
//출력: bean = class hello.core.AppConfig$$EnhancerBySpringCGLIB$$bd479d70
}
- 위의 코드는 결과가 이렇게 나온다.
- bean = class hello.core.AppConfig$$EnhancerBySpringCGLIB$$bd479d70
- 그러나 순수 클래스라면 다음과 같이 출력되어야 한다.
- class hello.core.AppConfig
- 결론부터 말하자면 내가 만든 클래스가 아니라 스프링이 CGLIB라는 바이트코드 조작 라이브러리를 이용하여 AppConfig 클래스를 상속받은 임의의 다른 클래스를 만들고, 그 다른 클래스를 스프링 빈에 등록한 것이다.
- 그 임의의 다른 클래스가 바로 싱글톤을 보장해준다.
실제로 보면 이런 로직이 나온다.
@Bean
public MemberRepository memberRepository() {
if (memoryMemberRepository가 이미 스프링 컨테이너에 등록되어 있으면?) {
return 스프링 컨테이너에서 찾아서 반환;
} else { //스프링 컨테이너에 없으면
기존 로직을 호출해서 MemoryMemberRepository를 생성하고 스프링 컨테이너에 등록
return 반환
}
}
@Configuration 적용하지 않고, @Bean만 적용한다면?
결론부터 말하자면 @Configuration은 바이트코드를 조작하는 CGLIB 기술을 사용하여 싱글톤을 보장한다.
만약 @Bean만 적용하면 빈은 생성되지만 싱글톤은 보장되지 않는다.
✔정리
- 스프링의 애플리케이션 환경은 다중의 사용자가 사용하므로 객체를 1번 생성하는 싱글톤 패턴을 사용한다.
- 싱글톤 패턴은 다양한 문제점이 있지만 싱글톤 컨테이너는 문제점을 해결하는 빈 방식을 사용한다.
- 이 방식 설계에서도 Data Less에 유의하여 무상태로 설계해야한다.
- @Configuration 애노테이션은 바이트 조작 CGLIB를 이용하여 싱글톤을 보장한다.
'Spring > 인프런_개념' 카테고리의 다른 글
| [Spring]스프링 핵심 원리 - 의존 관계 주입 (0) | 2023.12.28 |
|---|---|
| [Spring]스프링 핵심 원리 - 컴포넌트 (0) | 2023.12.27 |
| [Spring]스프링 핵심 원리 - 스프링 컨테이너와 빈 (0) | 2023.12.26 |
| [Spring]스프링 핵심 원리2 - 객체 지향 원리 적용 (2) | 2023.12.08 |
| [SOLID]좋은 객체 지향 설계의 5가지 원칙 (1) | 2023.12.06 |