본문 바로가기
SpringFramework/Spring 중요 개념

[Spring] @Configuration과 싱글톤

by 쭈봉이 2021. 12. 17.

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

 

댓글