스프링 핵심 원리 이해 - 싱글톤 컨테이너 (2)

꾸준하게 달리기~·2023년 8월 19일
0

스프링 + 자바

목록 보기
16/20
post-thumbnail

들어가기 앞서

해당 글은,
https://velog.io/@dlsrjsdl6505/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EC%9D%B4%ED%95%B4-%EC%8B%B1%EA%B8%80%ED%86%A4-%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88
에 이어져서 작성됩니다!~ :)

그럼, Spring은 순수자바 싱글톤 코드도 없으면서,
어떻게 자체적으로 Bean들을 싱글톤으로 제공할 수 있는걸까?




@Configuration과 싱글톤

작성된 AppConfig는 다음과 같다.

@Configuration
public class AppConfig {


    @Bean
    public MemberService memberService(){
        return new MemberServiceImpl(memberRepository());
    }
    
    @Bean
    public MemoryMemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }


    @Bean
    public OrderService orderService(){
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }

    @Bean
    public DiscountPolicy discountPolicy(){
        return new RateDiscountPolicy();
    }
}

해당 코드를 보면,

음, orderService, memberService 둘 다 memberRepository매서드를 사용하네.
근데 memberRepository 매서드는
return new MemoryMemberRepository(); 인데,
oerderService, memberService 매서드를 사용할때마다
각각 new MemoryMemberRepository(); 가 실행되어
싱글톤이 안되고 여러개의 MemoryMemberRepository 가 생기는게 아닐까?

라는 의문이 들 수 있다.

해당 의문 해소를 위해, orderService와 memberService에
해당 인스턴스의 memberRepository를 반환하는 매서드를 작성한 후,

//싱글톤 테스트용, memberService와 orderService 둘 다에 작성
    public MemberRepository getMemberRepository() {
        return this.memberRepository;
    }

테스트코드를 작성했다.

	@Test
    @DisplayName("과연 스프링은 AppConfig에서 new로 여러번 생성되는 객체들도 싱글톤으로 관리할것인지..?")
    void configuationTest(){
        ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

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

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

        System.out.println("memberService → memberServiceRepository = " + memberRepository1);
        System.out.println("orderService → memberServiceRepository = " + memberRepository2);

        //확인해보면 memberRepository 인스턴스는 모두 같은 인스턴스가 공유되어 사용된다.
    }

결과는, 아래와 같다.

즉, memberService나 orderService나
return new MemortMemberReopository(); 가 되어도,
똑같은 memberRepository를 갖게 되는 것이다.

뭐야, 어떻게 이럴 수 있지?
아래에 설명이 있다.



@Configuration의 바이트코드 조작

다시 위에서 AppConfig 클래스를 보고 오시면,
클래스단위에 @Configuration 어노테이션을 볼 수 있다.

답은 그곳에 있다!
스프링이 CGLIB라는 바이트코드 조작 라이브러리를 사용해서 @Configuration이 붙은 AppConfig 클래스를 상속받은
임의의 다른 클래스를 만들고, 그 다른 클래스를 스프링 빈으로 등록한 것이다.

그림은 다음과 같다.

But,
해당 바이트코드 조작 내부 기술은 매우 복잡하다고 한다..ㅠㅠ

즉, 내부적으로
Spring이 @Configuration이 붙은 클래스 차원에서
해당 클래스를 상속한 같은 클래스를 만들고,
그 같은 클래스 안에서는 기존의 클래스와 다르게
@Bean 이 붙은 스프링 빈들을 사용할 때
생성되어있으면 그대로 return,
생성되어있지 않으면 생성하고 컨테이너에 등록
이라는 로직이 동적으로 만들어져 장착된다.
라고 생각하면 되겠다.


ps.
스프링 프레임워크의 동작 방식도 뜯어보면 가끔 재밌다는 생각이 든다.
모르는것을 알아가는 즐거움인가..?

소스코드 : https://github.com/ingeon2/coreofspring-SOLID
레퍼런스 : 김영한님 pdf

profile
반갑습니다~! 좋은하루 보내세요 :)

0개의 댓글