작성
·
330
1
복습을 하다가 @Configuration에 대해서 복습을 하다 이상한 점이 발견되어 질문 드립니다.
@Configuration을 클래스 위에 붙이면 해당 클래스를 스프링 컨테이너에서 싱글톤으로 관리한다고 들었습니다. 그런데 StatefulServiceTest 코드의 TestConfig에서 @Configuration을 제거한 상태로 테스트를 돌려보니 같은 객체가 리턴되는 것을 보고 AppConfig에서도 @Configuration을 제거하고 ConfigurationSingletonTest 테스트를 실행해보았더니 다른 객체가 리턴되어서 어떤 이유인지 궁금해 질문 올립니다. 아래는 해당 코드들 입니다.
StatefualServiceTest
package hello.core.singleton;
import hello.core.member.MemberRepository;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.member.MemoryMemberRepository;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;
class StatefulServiceTest {
@Test
void StatefulServiceSingleton() {
ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
StatefulService statefulService1 = ac.getBean(StatefulService.class);
StatefulService statefulService2 = ac.getBean(StatefulService.class);
// A 사용자 10000원 주문
statefulService1.order("userA", 10000);
// B 사용자 20000원 주문
statefulService2.order("userB", 20000);
System.out.println("statefulService1 = " + statefulService1);
System.out.println("statefulService2 = " + statefulService2);
MemberServiceImpl bean1 = ac.getBean(MemberServiceImpl.class);
MemberServiceImpl bean2 = ac.getBean(MemberServiceImpl.class);
System.out.println("bean1 = " + bean1.getMemberRepository());
System.out.println("bean2 = " + bean2.getMemberRepository());
// A사용자 주문 금액 조회
int price = statefulService1.getPrice();
System.out.println("price = " + price);
assertThat(statefulService1.getPrice()).isEqualTo(20000);
}
static class TestConfig {
@Bean
public StatefulService statefulService() {
return new StatefulService();
}
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
@Bean MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
}
테스트 실행 시 로그
ConfigurationSingletonTest
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("memberService -> memberRepository1 = " + memberRepository1);
System.out.println("orderService -> memberRepository2 = " + memberRepository2);
System.out.println("memberRepository = " + memberRepository);
Assertions.assertThat(memberService.getMemberRepository()).isSameAs(memberRepository);
Assertions.assertThat(orderService.getMemberRepository()).isSameAs(memberRepository);
}
@Test
void configurationdeep() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
AppConfig bean = ac.getBean(AppConfig.class);
System.out.println("bean = " + bean.getClass());
}
}
테스트 실행 시 로그
AppConfig
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
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();
}
}
답변 1
3
안녕하세요. 박정우님, 공식 서포터즈 David입니다.
TestConfig 내에서는 memberRepository()가 MemberService를 생성할 때 한 번 호출됩니다. 즉, MemberService가 생성될 때 주입된 memberRepository 객체는 MemberService 빈 내에 할당된 상태이며 변경되지 않습니다. 따라서, MemberService를 컨테이너로부터 찾아와서 getMemberRepository()를 호출하면 동일한 객체만을 반환할 수 밖에 없습니다.
다르게 나오는 걸 보고 싶으시다면 TestConfig 내에서 memberRepository()를 여러 번 호출하는 코드를 작성해보시길 권장합니다.
또한 ac.getBean(MemberRepository.class)를 하셔서 반환되는 객체와도 비교해보시면 좋을 것 같습니다.
감사합니다.
해결된 것 같습니다. 좋은 정보 주셔서 감사드립니다..!