싱글톤 컨테이너
- 스프링 컨테이너는 객체 인스턴스를 싱글톤으로 관리한다.
- 스프링 컨테이너는 싱글톤 컨테이너 역할을 한다.
(싱글톤 레지스트리 : 싱글톤 객체를 생성하고 관리하는 기능) - 해당 기능 덕분에 싱글톤 패턴의 단점을 해결하며, 객체를 싱글톤으로 유지할 수 있다.
싱글톤 패턴의 단점
- 구현 코드가 길다
- 인스턴스 생성 시 구체클래스에 의존한다. (DIP위반 및 OCP를 위반할 가능성)
- 테스트가 어려움
- 내부 속성 변경 또는 초기화가 어려움
- 유연성이 떨어지며, 안티패턴으로 불리기도 한다.
@Configuration
@Configuration 어노테이션은 인스턴스를 싱글톤으로 관리하는 기능을 제공한다.
아래 코드를 먼저 살펴보자
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
//1번
System.out.println("call AppConfig.memberService");
return new MemberServiceImpl(memberRepository());
}
@Bean
public OrderService orderService() {
//1번
System.out.println("call AppConfig.orderService");
return new OrderServiceImpl(
memberRepository(),
discountPolicy());
}
@Bean
public MemberRepository memberRepository() {
//2번? 3번?
System.out.println("call AppConfig.memberRepository");
return new MemoryMemberRepository();
}
@Bean
public DiscountPolicy discountPolicy() {
return new RateDiscountPolicy();
}
}
해당 java 코드의 예상 진행은
1. memberService()호출 -> call AppConfig.memberService 출력
2. memberRepository()호출 -> call AppConfig.memberRepository 출력
3. orderService()호출 -> call AppConfig.orderService출력
4. memberRepository()호출 -> call AppConfig.memberRepository 출력
5. memberRepository()호출 -> call AppConfig.memberRepository 출력
따라서 최종 출력은 아래와 같이 예상된다.
call AppConfig.memberService
call AppConfig.memberRepository
call AppConfig.orderService
call AppConfig.memberRepository
call AppConfig.memberRepository
하지만 실제 결과는?
call AppConfig.memberService
call AppConfig.memberRepository
call AppConfig.orderService
결과가 이렇게 출력되는 이유는 @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$$bd479d70
}
먼저 스프링 컨테이너를 생성하며 인자로 넘긴 설정파일은 스프링 빈으로 등록된다.
따라서 AppConfig.class도 스프링 빈으로 등록된다.
이를 출력해보면
bean = class hello.core.AppConfig$$EnhancerBySpringCGLIB$$bd479d70
와 같은 결과를 얻을 수 있다.
만약 생성된 빈이 순수한 클래스라면
bean = class hello.core.AppConfig
로 출력되어야 한다.
하지만 예상과 다르게 클래스면에 xxxCGLIB이라는 다른 내용이 붙어있다.
이것은 내가 직접 만든 클래스가 아니며,
스프링이 CGLIB이라는 바이트코드 조작 라이브러리를 사용해,
AppConfig 클래스를 상속받은 임의의 다른 클래스를 만들고
생성한 자식 클래스를 스프링 빈으로 등록한 것이다.
이때 생성된 클래스가 싱글톤을 보장해준다.
AppConfig@CGLIB 예상 코드
@Bean
public MemberRepository memberRepository() {
if (이미 스프링 컨테이너에 등록되어 있으면?) {
return 스프링 컨테이너에서 찾아서 반환;
} else { //스프링 컨테이너에 없으면
작성한 로직을 호출해 스프링 컨테이너에 등록
return 반환
}
}
- @Bean이 붙은 메서드마다 bean이 존재하면 반환, 없으면 생성하는 코드가 동적으로 만들어진다.
여기서 만약 @Configuration을 없애고, 같은 코드를 실행시키면 어떻게 될까
위에서 예상했던 결과가 그대로 출력된다.
추가로 계속하여 new로 객체를 반환하기 때문에 싱글톤 패턴도 적용되지 않는다.
call AppConfig.memberService
call AppConfig.memberRepository
call AppConfig.orderService
call AppConfig.memberRepository
call AppConfig.memberRepository
세가지 객체 모두 다른 memberRepository를 참조한다.
'Spring' 카테고리의 다른 글
스프링 컨테이너와 스프링 빈 (1) | 2024.11.30 |
---|---|
[Spring] SRP, DIP, OCP 적용하기 (0) | 2024.11.28 |