Configuration 애너테이션
인프런 스프링 핵심 원리 - 기본편 수강 중
Configuration 애너테이션과 싱글톤
AppConfig
@Configuration
public class AppConfig {
    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }
    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }
    @Bean
    public OrderService orderService() {
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }
    @Bean
    public DiscountPolicy discountPolicy(){
        return new RateDiscountPolicy();
    }
}
memberService빈:memberRepository()호출new MemoryMemberRepository()호출
orderService빈:memberRepository()호출new MemoryMemberRepository()호출
- 2개의 
MemoryMemberRepository가 생성되면 싱글톤이 깨질까? 
테스트 코드
검증용으로 임시 코드 추가
public class MemberServiceImpl implements MemberService {
    private final MemberRepository memberRepository;
    //테스트 용도
    public MemberRepository getMemberRepository() {
        return memberRepository;
    }
}
public class OrderServiceImpl implements OrderService {
    private final MemberRepository memberRepository;
    //테스트 용도
    public MemberRepository getMemberRepository() {
        return memberRepository;
    }
}
테스트 코드
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);
        MemberRepository memberRepository1 = memberService.getMemberRepository();
        MemberRepository memberRepository2 = orderService.getMemberRepository();
        System.out.println("memberService -> memberRepository = " + memberRepository1);
        System.out.println("orderService -> memberRepository = " + memberRepository2);
        System.out.println("memberRepository = " + memberRepository);
        Assertions.assertThat(memberService.getMemberRepository()).isSameAs(memberRepository);
        Assertions.assertThat(orderService.getMemberRepository()).isSameAs(memberRepository);
    }
}
AppConfig에 로그 추가
@Bean
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());
}
예상 결과
- 총 3번
- 스프링 컨테이너가 스프링 빈에 등록하기 위해 
memberRepository()호출 memberService()로직에서memberRepository()호출orderService()로직에서memberRepository()호출
 - 스프링 컨테이너가 스프링 빈에 등록하기 위해 
 
실제 결과
- 1번 호출
 
@Configuration 바이트코드 조작
스프링이 자바 코드를 조작할 수는 없음
테스트 코드
@Test
void configurationDeep(){
    ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
    AppConfig bean = ac.getBean(AppConfig.class);
    System.out.println("bean = " + bean.getClass());
}
AnnotationConfigApplicationContext에 파라미터로 넘겨AppConfig도 스프링 빈이 됨AppConfig클래스 출력 결과:bean = class hello.core.AppConfig$$EnhancerBySpringCGLIB$$993824f4- 뒤에 이상한 값들이 붙음
 

- 스프링이 
CGLIB라는 바이트코드 조작 라이브러리를 사용해AppConfig클래스를 상속받은 임의의 다른 클래스를 만들어 스프링 빈에 등록한 것 - 이 임의의 다른 클래스가 싱글톤을 보장
 
AppConfig@CGLIB 예상 코드
@Bean
public MemberRepository memberRepository() {
    
    if (memoryMemberRepository가 이미 스프링 컨테이너에 등록되어 있으면?) {
        return 스프링 컨테이너에서 찾아서 반환;
    } else { //스프링 컨테이너에 없으면
        기존 로직을 호출해서 MemoryMemberRepository를 생성하고 스프링 컨테이너에 등록
        return 반환
    }
}
- @Bean이 붙은 메서드마다 이미 스프링 빈이 존재하면 존재하는 빈을 반환
 - 없으면 생성해서 스프링 빈으로 등록하고 반환 코드가 동적으로 만들어짐
 AppConfig@CGLIB는AppConfig의 자식 타입이기 때문에AppConfig타입으로 조회 가능하고 문제 없이 동작
@Configuration을 제거한다면?
- 순수 AppConfig로 스프링 빈 등록
bean = class hello.core.AppConfig - 싱글톤이 꺠짐
 - 전에 예상했던대로 
memberRepository3번 출력call AppConfig.memberService call AppConfig.memberRepository call AppConfig.orderService call AppConfig.memberRepository call AppConfig.memberRepository - @Bean만 사용해도 스프링 빈으로 등록되지만 싱글톤은 보장되지 않음
 
[참조] 인프런 스프링 핵심 원리 - 기본편 - 링크
끝!