본문 바로가기

프로그래밍/- Spring

[ 싱글톤 컨테이너 ] @Configuration과 싱글톤, @Configuration과 바이트코드 조작의 마법

 

 

@Configuration과 싱글톤

 

 

➡️ 의문점 : 각 Bean에서 같은 인스턴스가 생성되며(MemoryMemberRepository) 싱글톤이 깨지는 것 처럼 보임

@Bean   //스프링 컨테이너에 등록
public MemberService memberService(){   //호출 시 MemoryMemberRepository()호출
    return new MemberServiceImpl(memberRepository());
}
@Bean
public OrderService orderService(){     //호출 시 MemoryMemberRepository()호출
    return new OrderServiceImpl(
            memberRepository(),
            discountPolicy()
    );
}

 

➡️ 하지만 조회해보면 싱글톤이 유지되는 것을 알 수 있음 >> Configuration의 역할

@Bean
public MemberRepository memberRepository() {
    System.out.println("call AppConfig.memberRepository");  //세 번 호출될 것 같지만 한 번만 호출됨(Configuration의 힘)
    return new MemoryMemberRepository();    // 데이터 저장 수정시 해당 부분만 수정
}
package hello.core.singleton;

import hello.core.AppConfig;
import hello.core.member.MemberRepository;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.order.OrderServiceImpl;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import static org.assertj.core.api.Assertions.*;

public class ConfigurationSingletonTest {

    @Test
    void configurationTest(){
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

        MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);
        OrderServiceImpl orderService = ac.getBean("orderService", OrderServiceImpl.class);
        MemberRepository memberRepository = ac.getBean("memberRepository", MemberRepository.class);

        MemberRepository memberRepository1 = memberService.getMemberRepository();
        MemberRepository memberRepository2 = orderService.getMemberRepository();

        System.out.println("memberService -> memberRepository1 = " + memberRepository1);
        System.out.println("orderService -> memberRepository2 = " + memberRepository2);
        System.out.println("memberRepository = " + memberRepository);
        // 모두 같은 인스턴스 조회됨

        assertThat(memberService.getMemberRepository()).isSameAs(memberRepository);
        assertThat(orderService.getMemberRepository()).isSameAs(memberRepository);
        // 모두 같은 인스턴스 참고함

    }
}

 

 

 

 

 

 

@Configuration과 바이트코드 조작의 마법

 

 

➡️ 스프링이 싱글톤을 보장해야 하지만 자바 코드까지 조작하기는 어렵다.

 

➡️ AppConfig를 조회해보면 순수 클래스가 아닌 xxxCGLIB가 붙은 클래스로 조회됨

  🔸내가 만든 클래스가 아니라, 스프링이 CGLIB라는 바이트코드 조작 라이브러리로 임의의 클래스를 생성하고 상속해서 등록 >> 싱글톤을 보장해 줌

  🔸상속했기 때문에 AppConfig로 조회해도 조회가 가능(자식이기 때문에)

 
 
 

@Test
void configurationDeep(){
    ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
    AppConfig bean = ac.getBean(AppConfig.class);

    System.out.println("bean = " + bean.getClass());
}

 

 

➡️ AppConfig@CGLIB 예상 코드

 @Bean
 public MemberRepository memberRepository() {
 if (memoryMemberRepository가 이미 스프링 컨테이너에 등록되어 있으면?) {
 return 스프링 컨테이너에서 찾아서 반환;
    } else { //스프링 컨테이너에 없으면
기존 로직을 호출해서 MemoryMemberRepository를 생성하고 스프링 컨테이너에 등록
return 반환
}
}

 

 

➡️ 만약 @Configuration이 없이 @Bean만 적용한다면?

  🔸빈 등록은 가능

  🔸싱글톤을 보장하지 않음 (CGLIB가 아닌 순수 클래스가 등록됨)

 

 

 

 

 

 

 

* 김영한님의 스프링 핵심 원리 - 기본편을 요약 및 정리한 내용입니다 *

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8