김영한님의 스프링 핵심 원리 - 기본편 강의
섹션5. 싱글톤 컨테이너 강의 요약입니다.
@Configuration과 싱글톤
아래 코드를 보자. BEAN이 어떤 순서로 생성될까? 순차적으로 scan하니 우리가 예상하는 대로 작성해보자.
예상되는 호출 순서
call AppConfig.memberService // memberService에서
call AppConfig.memberRepository // memberRepository호출하고,
call AppConfig.memberRepository // memberRepository 가 또 호출되고
call AppConfig.orderService // orderService 가 호출되면서
call AppConfig.memberRepository //memberRepository가 호출된다.
@Configuration
public class AppConfig {
/**
* 생성자 주입
*/
@Bean
public MemberService memberService() {
System.out.println("call AppConfig.memberService");
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
System.out.println("call AppConfig.memberRepository");
return new MemoryMemberRepository();
}
@Bean
public OrderService orderService() {
System.out.println("call AppConfig.orderService");
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
...
}
AppConfig.class
에서 각각 두개의 MemoryMemberRepository를 생성하면서 Singleton이 깨지는 것처럼 보인다. 그러나 테스트 코드를 돌리면 MemoryMemberRepository가 한 번만 호출되고, 모두 같은 인스턴스가 공유되어 사용된다.
// 다음 코드를 실행해보자.
@Test
void configurationDeep() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
AppConfig bean = ac.getBean(AppConfig.class);
System.out.println("bean = " + bean.getClass());
}
결과
AppConfig자바 코드를 보면 분명히 다른 인스턴스가 생성되는데 로그를 출력하면 bean 생성시 call AppConfig.memberRepository
한 줄만 출력된다.
call AppConfig.memberService
call AppConfig.memberRepository
call AppConfig.orderService
@Configuration과 바이트코드 조작의 마법
AppConfig bean 클래스 이름을 출력해보면 다음과 같은 클래스 이름이 출력된다. bean = class hello.core.AppConfig$$SpringCGLIB$$0
내가 만든 것은 AppConfig 자체인데, 뒤에 SpringCGLIB가추가로붙어있다.(참고로은 @ 와 같다.)
스프링이 CGLIB 라는 바이트코드 조작 라이브러리를 사용해서 AppConfig클래스를 상속받은 임의의 다른 클래스를 만들고,그 다른 클래스를 스프링 빈으로 등록한 것이다.
즉, 스프링 컨테이너에 등록된 AppConfig는 이름은 AppConfig
인데 instance가 AppConfig$$SpringCGLIB
이다.
AppConfig$$SpringCGLIB
- 임의의 다른 클래스가 바로 싱글톤이 보장되도록 해준다.
- 바이트 코드를 조작해서 작성되어 있을 것이다. (실제로는 매우 복잡)
- @Bean이 붙은 메서드마다 스프링 빈이 존재하면 존재하는 빈 반환, 없으면 생성해서 스프링 빈으로 등록하고 반환하는 코드가 동적으로 만들어짐
- 덕분에 싱글톤이 보장된다.
AppConfig$$SpringCGLIB 예상 코드
@Bean
public MemberRepository memberRepository();
if (memberRepository가 이미 스프링 컨테이너에 등록되어 있으면)
return 스프링 컨테이너에서 찾아서 반환
else //스프링 컨테이너에 없으면
return 기존 로직을 호출해서 MemoryMemberRepository 생성하고 반환함
@Configuration 어노테이션을 제외해도 Bean 생성은 된다.
But 스프링 빈으로는 등록되지만, 싱글톤은 보장하지 않는다. 스프링 설정 정보는 항상 @Configuration을 사용하자!
@Configuration 제외하고, 같은 테스트 코드 실행했으나 싱글톤이 깨지는 것을 볼 수 있음(memberRepository가 3번 호출됨)
호출되는 bean도 AppConfig 그 자체임 -> bean = class hello.core.AppConfig
call AppConfig.memberService
call AppConfig.memberRepository
call AppConfig.memberRepository
call AppConfig.orderService
call AppConfig.memberRepository
// 다음 코드를 실행해보자.
@Test
void configurationDeep() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
AppConfig bean = ac.getBean(AppConfig.class);
System.out.println("bean = " + bean.getClass());
}
그리고 착한 Intellij는 @Configuration
을 제외했을 경우, 잘못되었다고 알려준다.
'Study > 인프런' 카테고리의 다른 글
스프링(Spring) 의존관계 주입 방법 4가지 (0) | 2023.03.17 |
---|---|
스프링 컴포넌트 스캔과 의존관계 자동 주입 (0) | 2023.02.11 |
스프링 개념 - 싱글톤 컨테이너란 (0) | 2023.02.05 |
BeanFactory와 ApplicationContext (0) | 2023.01.24 |
Spring Container에 등록된 모든 빈(Bean) 조회하기 (0) | 2023.01.24 |