Appconfig내에서
@Configuration
public class AppConfig {
@Bean
public MemberService memberService(){
return new MemberServiceImpl(memberRepository());
}
@Bean
public OrderService orderService(){
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
@Bean
public DiscountPolicy discountPolicy(){
return new RateDiscountPolicy();
// return new FixDiscountPolicy();
}
}
- memberService 빈을 만드는 코드를 보면 memberRepository()를 호출한다.
- 이메서드를 호출하면 new MemberRepository()를 호출한다.
- orderService 빈을 만드는 코드를 보면 memberRepository()를 호출한다.
- 이메서드를 호출하면 new MemberRepository()를 호출한다.
결과적으로 각각 다른 2개의 new MemberRepository()가 생성되면서 싱글톤이 깨지는것처럼보인다.
Test 코드를 작성하여 확인
MemberServiceImpl
public class MemberServiceImpl implements MemberService{
private final MemberRepository memberRepository;
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
//테스트 용도
public MemberRepository getMemberRepository() {
return memberRepository;
}
}
OrderServiceImpl
public class OrderServiceImpl implements OrderService{
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
//테스트 용도
public MemberRepository getMemberRepository() {
return memberRepository;
}
}
public class ConfigurationTest {
@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);
System.out.println("memberService -> memberRepository = " + memberRepository1);
System.out.println("orderService -> memberRepository = " + memberRepository2);
Assertions.assertThat(memberService.getMemberRepository()).isSameAs(memberRepository);
Assertions.assertThat(orderService.getMemberRepository()).isSameAs(memberRepository);
}
}
결과
memberRepository = hello.core.member.MemoryMemberRepository@95e33cc
memberService -> memberRepository = hello.core.member.MemoryMemberRepository@95e33cc
orderService -> memberRepository = hello.core.member.MemoryMemberRepository@95e33cc
확인결과 모든 memberRepository 인스턴스는 같은 객체를 가진다.
@Bean이 붙은 메서드마다 이미 스프링 빈이 존재하면 존재하는 빈을 반환하고, 스프링 빈이 없으면 생성해서 스프링 빈으로 등록하고 반홚나다.
=> 싱글톤이 보장된다.
AppCofig@CGLIB
순수한 클래스라면 'class.hello.core.AppCofig' 라고 출력이되어야 한다.
xxxCGLIB이 붙으면 내가만든 클래스가 아니라 스프링이 CGBLIB이라는 바이트 코드조작 라이브러리를 사용해서 AppConfig 클래스를 상속받은 임의의 다른 클랜스를 만들고, 그 다른 클래스를 스프링 빈으로 등록한 것이다.
그 임의의 다른 클래스가 바로 싱글톤이 보장되도록해준다.
※ @Configuration 을 적용하지않고 @Bean만적용시 스프링 빈 등록은 가능하다, 하지만 싱글톤 보장이 되지않는다.
Spring 설정정보 Class에는 @Configuration을 항상 사용하자!
결과
memberRepository = hello.core.member.MemoryMemberRepository@74287ea3
memberService -> memberRepository = hello.core.member.MemoryMemberRepository@7d7758be
orderService -> memberRepository = hello.core.member.MemoryMemberRepository@2bdd8394