코드 그라데이션

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

Spring/핵심 원리 구현

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

완벽한 장면 2024. 1. 29. 20:27

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

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

그런데 스프링이 자바 코드까지 어떻게 하기는 어렵다. 

앞선 자바 코드를 보면 분명 3번 호출되어야 하는 것이 맞다.

 

그래서 스프링은 클래스의 바이트코드를 조작하는 라이브러리를 사용한다.

모든 비밀은 `@Configuration` 을 적용한 `AppConfig` 에 있다.

 

 

테스트 코드 추가

package inflearn.spring_core.singleton;

import inflearn.spring_core.config.AppConfig;
import inflearn.spring_core.member.MemberRepository;
import inflearn.spring_core.member.MemberServiceImpl;
import inflearn.spring_core.order.OrderServiceImpl;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

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

public class ConfigurationSingletonTest {
    @Test
    void configurationTest() {

        ApplicationContext 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);

        // 각각의 서비스와 레포지토리가 같은 인스턴스를 참조하고 있는지 확인
        System.out.println("memberService -> memberRepository = " + memberService.getMemberRepository());
        System.out.println("orderService -> memberRepository = " + orderService.getMemberRepository());
        System.out.println("memberRepository = " + memberRepository);

        //모두 같은 인스턴스를 참고하고 있다.
        assertThat(memberService.getMemberRepository()).isSameAs(memberRepository);
        assertThat(orderService.getMemberRepository()).isSameAs(memberRepository);
    }

    // 추가
    @Test
    void configurationDeep() {
        ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

        //AppConfig도 스프링 빈으로 등록된다.
        AppConfig bean = ac.getBean(AppConfig.class);
        System.out.println("bean = " + bean.getClass());

    }

}

 

실행 결과를 보면

이 노란줄 쳐놓은 빈 출력 결과가 조금 특이하다!

 

순수한 클래스라면 다음과 같이 출력되어야 한다.

`class hello.core.AppConfig`

 

그런데 예상과는 다르게 클래스명에 xxxCGLIB이 붙으면서 상당히 복잡해진 것을 볼 수 있다.

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

 

그림

 

아마도 다음과 같이 바이트 코드를 조작해서 작성되어 있을 것이다.

(실제로는 CGLIB의 내부 기술을 사용하는데 매우 복잡하다.)

\

 

AppConfig@CGLIB 예상 코드

 

**참고** AppConfig@CGLIB는 AppConfig의 자식 타입이므로, AppConfig 타입으로 조회 할 수 있다

 


`@Configuration` 을 적용하지 않고, `@Bean` 만 적용하면?

AppConfig에서 //@Configuration 삭제 

 

실행하면

앞과 달리 memberRepository 가 총 3번 호출되었음.

 

테스트 코드를 다시 실행해보면

에러 나온다.

 

다시 @Configuration 살려놓는다!

 


정리

 

728x90

'Spring > 핵심 원리 구현' 카테고리의 다른 글

탐색 위치  (0) 2024.01.31
컴포넌트 스캔과 의존관계 자동 주입 시작하기  (0) 2024.01.30
@Configuration과 싱글톤  (0) 2024.01.29
싱글톤 방식의 주의점  (0) 2024.01.27
싱글톤 컨테이너  (0) 2024.01.27
Comments