@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번만 호출되었다.
스프링이 어떠한 방법을 쓰는지는 모르겠지만
싱글톤을 보장해준다는 것을 알 수 있었다..
'웹프로그래밍 > Spring 핵심 원리' 카테고리의 다른 글
32. 컴포넌트 스캔과 의존관계 자동 주입 시작하기 (0) | 2021.08.03 |
---|---|
31. @Configuration과 바이트코드 조작의 마법 (0) | 2021.07.30 |
29. 싱글톤 방식의 주의점 (0) | 2021.07.30 |
28. 싱글톤 컨테이너 (0) | 2021.07.23 |
27. 싱글톤 패턴 (0) | 2021.07.22 |