스프링 - 싱글톤 컨테이너

meluu_·2023년 8월 10일
0

스프링

목록 보기
3/27
post-thumbnail

🌿 웹 애플리케이션과 싱글톤


웹 애플리케이션은 보통 여러 고객이 동시에 요청한다.
스프링 없는 순수한 DI 컨테이너인 AppConfig를 사용한다 했을 때

  • 고객 트래픽이 초당 100이 나오면 초당 100개 객체가 생성되고 소멸 -> 메모리 낭비
  • 해결 방안 : 해당 객체가 딱 1개만 생성되고 공유하도록 설계 -> 싱글톤 패턴

☘️ 싱글톤 패턴


// static영역에 1개만 생성
private static final SingletonService instance = new SingletonService();

// 해당 메소드로 객체 인스턴스 사용 -> 싱글톤
public static SingletonService getInstance() {
 return instance;
 }
 
 // 생성자 private로 방지
 private SingletonService() {
 }
  • 클래스의 인스턴스가 단 '1'개만 생성되는 것을 보장하는 디자인 패턴
  • private 생성자를 사용해서 외부에서 임의로 new 키워드 사용 방지

고객의 요청이 올 때 마다 객체 생성이 아닌 만들어진 객체를 공유, 효율적으로 사용

✔️ 싱글톤 패턴 문제점

  • 싱글톤 패턴 구현 코드 자체가 많이 들어감
  • 의존관계상 클라이언트가 구체 클래스에 의존 -> DIP 위반
  • 클라이언트가 구체 클래스에 의존 -> OCP 위반 가능성 높음
  • 테스트 어려움
  • 내부 속성을 변경, 초기화 어려움
  • private 생성자로 자식클래스 생성 어려움
  • 결론적으로 유연성이 떨어짐
  • 안티 패턴으로 불리기도 함

☘️ 싱글톤 컨테이너


스프링 컨테이너: 싱글톤 패턴의 문제점 해결 및 객체 인스턴스를 싱글톤으로 관리

✔️ 싱글톤 컨테이너

  • 싱글턴 패턴을 적용하지 않아도, 객체 인스턴스를 싱글톤으로 관리

    • 컨테이너는 객체 하나만 생성하고 관리
  • 싱글톤 컨테이너 역할 수행

    • 싱글톤 객체를 생성,관리 하는 기능 : 싱글톤 레지스트리
  • 이런 기능 덕분에 싱글톤 패턴의 모든 단점을 해결 및 객체를 싱글톤으로 유지

    • 싱글톤 패턴을 위한 지저분한 코드 X

    • DIP, OCP, 테스트, private 생성자로 부터 자유롭게 싱글톤 사용가능

ApplicationContext ac = new
				AnnotationConfigApplicationContext(AppConfig.class);

Menu menu1 =ac.getBean("Menu", Menu.class);
Menu menu2 =ac.getBean("Menu", Menu.class);

// menu1 == menu2
assertThat(menu1).isSameAs(menu2);

테스트 코드에서 Assertions 사용한다.
jnuit 과 assertj 가 있다.

스프링의 기본 빈 등록 방식은 싱글톤 이지만 다른 방식도 지원한다.
ex) 프로토 타입 (요청할때마다 새로운 객체 반환)


✔️ 싱글톤 방식의 주의점


  • 히나의 객체 인스턴스를 공유하기 떄문에 싱글톤 객체는 무상태 설계 해야한다.

    • 특정 클라이언트에 의존적인 필드가 없어야한다.

    • 특정 클라이언트가 값을 변경할 수 있는 필드가 없어야 한다.

    • 가급적 읽기만 가능해야한다.

    • 필드 대신에 자바에서 공유되지 않는, 지역변수, 파라미터, ThreadLocal등을 사용해야 한다.

  • 스프링 빈의 필드에 공유 값을 설정하면 큰 장애가 발생할 수 있다.

스프링 빈은 항상 무상태 설계를 기억하자


☘️ @Configuration과 싱글톤


@Configuration
public class AppConfig {
 	
    @Bean
 	public MemberService memberService() {
 		return new MemberServiceImpl(memberRepository());
 	}
    
 	@Bean
 	public OrderService orderService() {
 		return new OrderServiceImpl(memberRepository(),discountPolicy());
 	}
    
 	@Bean
 	public MemberRepository memberRepository() {
 		return new MemoryMemberRepository();
 	}
 ...
}

여기서 보면 memberRepository()를 MemberService에서 1번 OrderService에서 1번씩 총 2번 호출하여 MemoryMemberRepository객체가 2개 생긴거 아닌가 싶다.

하지만 @Configuration의 기능으로 싱글톤이 유지된다.


🧬 @Configuration과 바이트코드 조작


  • 스프링 컨테이너는 스프링 빈이 싱글톤이 되도록 보장해야한다.

  • 스프링은 자바 코드까지는 건드릴 수 없어서
    스프링 클래스의 바이트 코드를 조작하는 라이브러리를 사용한다.

  • @Configuration이 핵심이다.

여기서 AppConfig 스프링 빈을 조회해서 클래스 정보를 출력해보면


@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$$bd312d65

스프링이 CGLIB라는 바이트 코드 조작 라이브러리를 사용해서
AppConfig 클래스를 상속받은 임의의 다른 클래스를 만들고
그 클래스를 스프링 빈으로 동록한다.

  • @Bean이 붙은 메서드마다 스프링 빈이 존재하면 존재하는 빈 반환

  • 빈이 없으면 생성해서 스프링 빈으로 등록 후 반환 하는 코드가 동적으로 생성됨

  • 덕분에 싱글톤이 보장


❓ @Configuration 을 적용하지 않고, @Bean 만 적용?


결과적으로는 바이트 코드 조작 라이브러리가 작동하지 않고
요청하는 대로 객체가 생성되어 싱글톤이 파괴된다.

  • @Bean만 사용시 스프링 빈 등록은 되지만, 싱글톤 보장 X
  • 스프링 설정 정보는 항상 @Configuration을 사용하자!

🔖 학습내용 출처

스프링 핵심 원리 - 기본편

profile
열심히 살자

0개의 댓글