[Spring] @Configuration과 싱글톤

[Spring] @Configuration과 싱글톤

AppConfig

memberRepository() -> new MemoryMemberRespository 객체를 새로 생성하는 메서드

1. memberService() -> memberRepository -> new MemoryMemberRespository 사용

2. orderService ()-> memberRepository -> new MemoryMemberRespository 사용

1번에서 사용되는 MemoryMemberRespository와 2번에서 사용된 MemoryMemberRespository는

서로 다른 객체이지 않을까? (둘다 메서드를 사용할 때 new 키워드로 새로 생성되기 때문에)

만약 다른 객체라면 싱글톤 패턴에 위배되는 현상!

[테스트 결과]

@Test void configurationTest() { AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class); MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class); OrderServiceImpl orderService = ac.getBean("orderService", OrderServiceImpl.class); MemoryMemberRepository memberRepository = ac.getBean("memberRepository", MemoryMemberRepository.class); MemberRepository memberRepository1 = memberService.getMemberRepository(); MemberRepository memberRepository2 = orderService.getMemberRepository(); System.out.println("memberService = " + memberRepository1); System.out.println("orderService = " + memberRepository2); System.out.println("MemoryMemberRepository = " + memberRepository); // memberService = hello.core.member.MemoryMemberRepository@333dd51e // orderService = hello.core.member.MemoryMemberRepository@333dd51e // MemoryMemberRepository = hello.core.member.MemoryMemberRepository@333dd51e Assertions.assertThat(memberService.getMemberRepository()).isSameAs(memberRepository); Assertions.assertThat(orderService.getMemberRepository()).isSameAs(memberRepository);

놀랍게도 모두 같은 객체

심지어 MemoryMemberRepository를 직접 호출한 객체조차 모두 같은 객체였다.

AppConfig에 호출할때마다 로그를 남겨 확인해본 결과

call AppConfig.memberRepository

call AppConfig.memberService

call AppConfig.orderService

각각의 객체를 한번씩만 호출했다.

그 이유는 아래에 설명

@Configuration과 바이트코드 조작

스프링 컨테이너는 싱글톤 레지스트리다. 따라서 스프링 빈이 싱글톤이 되도록 보장해주어야한다.

@Test void configurationDeep() { ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class); AppConfig bean = ac.getBean(AppConfig.class); System.out.println("bean = " + bean.getClass()); //bean = class hello.core.AppConfig$$EnhancerBySpringCGLIB$$65898 }

순수한 클래스라면 class hello.core.AppConfig로 출력되어야 하는데 위와 같이 bean을 출력해보면

class hello.core.AppConfig$$EnhancerBySpringCGLIB$$65898라고 출력이된다.

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

즉, 내가 등록한 AppConfig는 사라지고 Spring이 직접 생성한 객체를 빈으로 등록하여 그 클래스를 사용하기 때문이다.

AppConfig@CGLIB (실제 사용) -> AppConfig

강의에서 추측하기로는

memoryMemberRepository가 이미 스프링 컨테이너에 등록되어 있으면, new 키워드를 사용하지 않고 스프링 컨테이너에서 찾아서 반환하고, 없으면 기존에 작성한 코드와 같이 객체를 생성하여 반환한다고 한다.

@Configuration을 적용하지 않고 @Bean만 적용하게 되면 아래와 같이

memberRepository를 매번 생성하게 된다.

즉, @Configuration은 싱글톤을 보장하도록 도와주는 애노테이션이다.

call AppConfig.memberRepository

call AppConfig.memberService

call AppConfig.memberRepository

call AppConfig.orderService

call AppConfig.memberRepository

memberService = hello.core.member.MemoryMemberRepository@535779e4

orderService = hello.core.member.MemoryMemberRepository@53fd0d10

MemoryMemberRepository = hello.core.member.MemoryMemberRepository@6c0d9d86

from http://ju-bong.tistory.com/30 by ccl(A) rewrite - 2021-12-17 23:01:33