@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
@Bean
public OrderService orderService() {
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
@Bean
public DiscountPolicy discountPolicy() {
return new RateDiscountPolicy();
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
MemoryMemberRepository
가 생성되면서 싱글톤이 깨지는 것 처럼 보인다.public class ConfigurationSingletonTest {
@Test
void configurationTest() {
AnnotationConfigApplicationContext 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 -> memberRepository1 = " + memberRepository1);
System.out.println("orderService -> memberRepository2 = " + memberRepository2);
System.out.println("memberRepository = " + memberRepository);
assertThat(memberService.getMemberRepository()).isSameAs(memberRepository);
assertThat(orderService.getMemberRepository()).isSameAs(memberRepository);
}
}
new MemoryMemberRepository
호출해서 다른 인스턴스가 생성되어야 하는데..?@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
System.out.println("Call AppConfig.memberService");
return new MemberServiceImpl(memberRepository());
}
@Bean
public OrderService orderService() {
System.out.println("Call AppConfig.orderService");
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
@Bean
public DiscountPolicy discountPolicy() {
return new RateDiscountPolicy();
}
@Bean
public MemberRepository memberRepository() {
System.out.println("Call AppConfig.memberRepository");
return new MemoryMemberRepository();
}
}
memberRepository()
는 다음과 같이 총 3번 호출되어야 하는 것이 아닐까?memberRepository()
호출memberRepository()
호출memberRepository()
호출call AppConfig.memberService
call AppConfig.memberRepository
call AppConfig.orderService
@Configuration
을 적용한 AppConfig
에 있다.public class ConfigurationSingletonTest {
@Test
void configurationDee() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
AppConfig bean = ac.getBean(AppConfig.class);
System.out.println("bean = " + bean.getClass());
}
}
AnnotationConfigApplicationContext
에 파라미터로 넘긴 값은 스프링 빈으로 등록된다.AppConfig
도 스프링 빈이 된다.AppConfig
스프링 빈을 조회하여 클래스 정보를 출력하면 다음과 같다.bean = class hello.core.config.AppConfig$$EnhancerBySpringCGLIB$$e4a3e35f
class hello.core.AppConfig
로 출력되어야 한다.@Bean
public MemberRepository memberRepository() {
if(memoryMemberRepository가 이미 스프링 컨테이너에 등록되어 있으면?) {
return 스프링 컨테이너에서 찾아서 반환;
} else // 스프링 컨테이너에 없으면
기존 로직을 호출해서 MemoryMemberRepository를 생성하고 스프링 컨테이너에 등록
return 반환
}
}
@Bean
이 붙은 메서드마다 이미 스프링 빈이 존재하면 존재하는 빈을 반환하고, 스프링 빈이 없으면 생성해서 스프링 빈으로 등록하고 반환하는 코드가 동적으로 만들어진다.AppConfig@CGLIB는 AppConfig의 자식 타입으로, AppConfig 타입으로 조회할 수 있다.
@Configuration
을 적용하지 않고, @Bean
만 적용하면 어떻게 될까?@Configuration
을 붙이면 바이트코드를 조작하는 CGLIB 기술을 사용해서 싱글톤을 보장하지만, 만약 @Bean
만 적용하면 어떻게 될까?
//@Configuration
public class AppConfig {
...
}
call AppConfig.memberService
call AppConfig.memberRepository
call AppConfig.orderService
call AppConfig.memberRepository
call AppConfig.memberRepository
bean = class hello.core.AppConfig
@Bean
만 사용해도 스프링 빈으로 등록되지만, 싱글톤을 보장하지 않는다.memberRepository()
처럼 의존관계 주입이 필요해서 메서드를 직접 호출할 때 싱글톤을 보장하지 않는다.@Configuration
을 사용하자.