[핵심] 스프링의 싱글톤 유지 방법 => @Configuration

Hyun·2023년 7월 21일
0

Spring

목록 보기
16/38

AppConfig@CGLIB 예상 코드

...
@Bean
public MemberRepository memberRepository(){

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

@Configuration

먼저, 스프링 컨테이너의 싱글톤 유지는 @Configuration 어노테이션이 있어 가능하다는 점을 알린다.

@Configuration 어노테이션이 적용된 "AppConfig"은 스프링 컨테이너에 관리되는 빈으로 등록되는것처럼 보이지만, 실제로 스프링은 CGLIB 를 사용하여 "AppConfig" 클래스를 상속받은 프록시 클래스인 "AppConfig@CGLIB" 클래스를 생성하고, 이 프록시 클래스를 빈으로 등록한다.

따라서 "AppConfig@CGLIB" 라는 클래스가 "AppConfig" 라는 이름의 빈으로 스프링 컨테이너에 등록되며, 이 프록시 클래스는 원본 "AppConfig" 를 확장하여 의존성 주입 등의 기능을 제공한다. "Bean" 으로 어노테이션된 메서드들은 이 프록시 클래스 내에서 호출되어 관리되고, 빈은 싱글톤으로 동작한다.

그럼 만약 @Configuration 어노테이션 없이 @Bean 만 있다면?

@Configuration 어노테이션 없이 @Bean 만 있다면, AppConfig@CGLIB 가 스프링 빈으로 등록되는게 아니라 AppConfig 가 스프링 빈으로 등록된다.

따라서 AppConfig@CGLIB 의 스프링 빈에 객체가 등록되어 있을때 등록된 객체를 내보내는 기능이 AppConfig 엔 없기 때문에 @Bean 에 객체가 등록되어도 그냥 새로운 객체를 생성하여 내보낸다. 따라서 싱글톤이 깨진다!

정리하자면, @Bean 만 사용해도 스프링 빈으로 등록되지만, 싱글톤을 보장하지는 않는다.

=> 스프링 설정 정보는 항상 "@Configuration" 을 사용하자!


@Bean 만 사용했을 때 싱글톤이 깨지는 경우의 예시)

AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);//빈 등록할때 한번씩 다 호출된다!

위와 같은 스프링 컨테이너 생성문이 호출되었을때,

아래의 첫번째 순서는 @Configuration + @Bean 이 있을때, 두번째 순서는 @Configuration 없이 @Bean 만 있을때이다.
*discountPolicy() 호출은 제외하였다.

순서1)
call AppConfig.memberService
calL AppConfig.memberRepository
call AppConfig.orderService
    
순서2)
call AppConfig.memberService
call AppConfig.memberRepository
call AppConfig.memberRepository
call AppConfig.orderService
call AppConfig.memberRepository

AppConfig.java

@Configuration
public class AppConfig {
    /*싱글톤이 아닐때 아래 메서드들을 한번씩 호출할때(discountPolicy 제외)*/
    //call AppConfig.memberService
    //call AppConfig.memberRepository
    //call AppConfig.memberRepository
    //call AppConfig.orderService
    //call AppConfig.memberRepository

    /*싱글톤일때 ...*/
    //call AppConfig.memberService
    //calL AppConfig.memberRepository
    //call AppConfig.orderService

    //생성자 주입 방법
    @Bean //스프링 컨테이너에 메서드이름을 key, 리턴값을 value 로 하여 등록됨
    public MemberService memberService(){//인터페이스(역할)
        System.out.println("call AppConfig.memberService");
        return new MemberServiceImpl(memberRepository());//구현클래스(구현)
    }

    @Bean
    public MemberRepository memberRepository() {//인터페이스(역할)
        System.out.println("call AppConfig.memberRepository");
        return new MemoryMemberRepository();//구현클래스(구현)
    }

    //생성자 주입 방법
    @Bean
    public OrderService orderService(){//인터페이스(역할)
        System.out.println("call AppConfig.orderService");
        return new OrderServiceImpl(memberRepository(), discountPolicy());//구현클래스(구현)
    }

    @Bean
    public static DiscountPolicy discountPolicy() {//인터페이스(역할)
        return new RateDiscountPolicy();//구현클래스(구현)
    }
}

질문 원문

본인
나는 AppConfig 클래스가 빈으로 등록되는게 아니라 AppConfig 클래스를 상속받은 AppConfig@CGLIB 클래스가 AppConfig 라는 이름으로 빈에 등록된다고 알고 있는데 내가 틀린거야?

ChatGPT
죄송합니다, 이해를 잘못하고 있었습니다. 당신이 설명하신 대로 맞습니다.

AppConfig 클래스가 @Configuration 어노테이션이 적용되어 있고, 스프링 컨테이너에서 관리되는 빈으로 등록됩니다. 그러나 실제로 스프링은 CGLIB를 사용하여 AppConfig 클래스를 상속받은 프록시 클래스인 AppConfig@CGLIB를 생성하고, 이 프록시 클래스를 빈으로 등록합니다.

따라서 AppConfig@CGLIB라는 클래스가 AppConfig라는 이름의 빈으로 스프링 컨테이너에 등록되며, 이 프록시 클래스는 원본 AppConfig를 확장하여 의존성 주입 등의 기능을 제공합니다. @Bean으로 어노테이션된 메서드들은 이 프록시 클래스 내에서 호출되어 관리되고, 빈은 싱글톤으로 동작합니다.

정확히 말씀드리면, AppConfig 클래스 자체는 빈으로 등록되지 않습니다. 대신, 이 클래스를 상속받은 프록시 클래스 AppConfig@CGLIB가 AppConfig라는 이름의 빈으로 등록되는 것이 맞습니다. 이 프록시 클래스는 스프링이 빈의 생성과 의존성 주입을 관리하는데 사용됩니다.

다시 한 번, 제가 이전에 드린 설명이 잘못되었고, 당신이 말씀하신 것이 맞습니다. 감사합니다.

profile
better than yesterday

0개의 댓글