@Configuration에 대해서 파헤쳐 보았다.

 

@Configuration은 싱글톤을 위해서 만들어졌다.

package hello.core;

import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.discount.RateDiscountPolicy;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.member.MemoryMemberRepository;
import hello.core.order.OrderService;
import hello.core.order.OrderServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {
    
    //@Bean memberService -> new MemoryMemberRepository() 
    //@Bean orderService -> new MemoryMemberRepository() 
    
    @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();
    }

}

AppConfig를 살펴보았다.

AppConfig는 순수한 자바코드이다.

memberService()를 호출하면 memberRepository에 의해서 MemoryMemberRepository가 생성된다.

orderService()를 호출하면 memberRepository에 의해서 또 MemoryMemberRepository가 생성된다.

 

엥? 그렇다면 싱글톤이 깨지는 것이 아닌가..?

 

 //Test
    public MemberRepository getMemberRepository() {
        return memberRepository;
    }

OrderServiceImpl , MemberServiceImpl 두 클래스에 이 코드를 추가한다.

 

package hello.core.singleton;

import hello.core.AppConfig;
import hello.core.member.MemberRepository;
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.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

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("memberRepository -> 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);

    }
}

 

3개의 객체를 비교하는 코드를 작성해보았다.

놀랍게도 3객체 모두 같은 객체인 것을 알 수있었다.

 

memberRepository -> memberRepository = hello.core.member.MemoryMemberRepository@1d730606
orderService -> memberRepository hello.core.member.MemoryMemberRepository@1d730606
memberRepository = hello.core.member.MemoryMemberRepository@1d730606

 

콘솔에 찍힌 출력이다.

 

AppConfig의 자바 코드를 보면 분명히 각각 3번 new MemoryMemberRepository를 호출하여 다른 인스턴스가 생성되야

하는데 실험을 통해서 확인해보았다.

 

package hello.core;

import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.discount.RateDiscountPolicy;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.member.MemoryMemberRepository;
import hello.core.order.OrderService;
import hello.core.order.OrderServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    //@Bean memberService -> new MemoryMemberRepository()
    //@Bean orderService -> new MemoryMemberRepository()

    @Bean
    public MemberService memberService(){
        System.out.println("call AppConfig.memberService");
        return new MemberServiceImpl(memberRepository());
    }

    @Bean
    public MemoryMemberRepository 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 DiscountPolicy discountPolicy(){
        return new RateDiscountPolicy();
    }

}

AppConfig클래스이다.

@Bean 안에

출력문을 통해 무엇이 호출되는지 알아보았다.

머리로 생각해보면

 

call AppConfig.memberService

call AppConfig.memberRepository

call AppConfig.memberRepository

call AppConfig.orderServiice

call AppConfig.memberRepository 

 

이렇게 총 5개가 되야 할것같은데 돌려보니

 

각각의 빈들 하나씩만 호출되어 3개가 되는 것을 알 수 있었다.

memberRepository는 3번호출되어야하는데 1번만 호출되었다.

 

스프링이 어떠한 방법을 쓰는지는 모르겠지만

싱글톤을 보장해준다는 것을 알 수 있었다..

+ Recent posts