인프런 커뮤니티 질문&답변

심모님의 프로필 이미지
심모

작성한 질문수

스프링 핵심 원리 - 기본편

@Configuration과 싱글톤

컨테이너를 통한 싱글톤에 대한 의문

해결된 질문

작성

·

375

13

아랫분들도 비슷한 질문을 해주셨는데, 그에 대한 답변이 제대로 이해가 안가서 다시한번 질문드립니다.

==============>질문을 드리는 과정에서 제가 이해한 내용이 있는데 이해한 내용이 맞는지 확인부탁드립니다!

제가 지금까지 수강들은 내용을 적자면

@Configuration 을 통해 컨테이너에 저장되면 같은 클래스에 대해 싱글톤이 유지 된다고 이해하였습니다.

그런데 제가 예제를 다시 돌아보면서 의문이 생겨 질문

기존 AppConfig에서 @Configuration을 주석을 하였습니다.

//@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 MemberRepository memberRepository() {
System.out.println("call AppConfig.memberRepository");
return new MemoryMemberRepository();
}

@Bean
public DiscountPolicy discountPolicy() {
return new RateDiscountPolicy();
}

}

이후 테스트 코드에서 두개의 객체를 생성해 값을 확인해보았습니다.

#1번테스트

    @Test
void springContainer(){

ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

//1. 조회 : 호출할 때 마다 객체 생성
MemberServiceImpl memberService1 = ac.getBean("memberService", MemberServiceImpl.class);

//2. 조회 : 호출할 때 마다 객체 생성
MemberServiceImpl memberService2 = ac.getBean("memberService",MemberServiceImpl.class);

//참조값이 같은 것 확인
System.out.println("memberService1 = " + memberService1);
System.out.println("memberService2 = " + memberService2);

// assertThat(memberService1).isSameAs(memberService2);
}

테스트 결과 두 개의 객체 memberService1과 memberService2의 참조값이 같다는걸 확인했습니다.

저는 이 결과를 보고 @Configuration이 없이 싱글톤이 유지되어 의문이 생겼습니다.

@Configuration을 여전히 주석한 상태로 다른 테스트인 

#2번테스트

    @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);

// assertThat(memberService.getMemberRepository()).isSameAs(memberRepository);
// assertThat(orderService.getMemberRepository()).isSameAs(memberRepository);
}

이 코드의 결과를 확인해보니 세개의 객체 memberRepository가 모두 다른 참조값을 가지고 있음을 확인했습니다.

그리고 다시 AppConfig.class에 @Configuration의 주석을 해제 후 같은 참조값을 가진것을 확인했습니다.

질문을 드리면서 제가 깨달은 내용은

#1번테스트와 #2번테스트 의 차이는

1번 테스트는 스프링컨테이너에 MemberService가 하나만 등록되었기 때문에 여러개를 생성해서 같은 참조값을 가지게 된거고

2번 테스트는 스프링컨테이너에 MemberRepository가 3가지의 객체로 저장되었기 때문에 3가지의 객체 모두 다른 참조값을 가지게 된거다.

따라서 @Configuration을 통해 스프링컨테이너에 MemberRepository를 싱글톤형태로 하나만 남게 된 것.

제가 이해한 내용이 맞나요?

제가 이해한 내용이 맞다면 제가 지금껏 잘못 생각한 내용은 @Configuration 이 없이 스프링컨테이너에  등록하게 되면 싱글톤 유지를 하지 못하는것은 맞는데 싱글톤의 범위(?)를 제가  잘못 이해하고 있었던 것 같습니다.

그냥 혼자 이해하고 말까라고 생각했다가 확인을 받고, 저랑 비슷하게 이해하셨던 분들이 있으신거 같아서 글 올립니다!

감사합니다!

답변 1

15

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. 심모님

먼저 각각의 빈들은 @Configuration이 있든 없든 싱글톤으로 유지됩니다.

그러면 왜 @Configuration을 해주어야 할까요?

먼저 스프링이 빈을 등록하면서 다음을 호출합니다.


@Bean
public MemberRepository memberRepository() {
System.out.println("call AppConfig.memberRepository");
return new MemoryMemberRepository();
}

그러면 여기에서 new MemoryMemberRepository() 객체가 새로 생성됩니다.

싱글톤이 깨지는 문제의 핵심은 바로 다음 부분에서 memberRepository()를 호출할 때 발생합니다.

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

이것은 그냥 자바 메서드 호출입니다. memberRepository() 이 메서드를 호출하면 내부에서 new MemoryMemberRepository()로 새로운 객체가 생성됩니다.

그런데 결과적으로 new MemoryMemberRepository()객체가 2개 생성된 것입니다.

이 문제를 해결해주는 것이 바로 @Configuration입니다.

이 애노테이션을 사용하면 memberRepository()와 같은 메서드 호출을 중간에 가로챌 수 있습니다. 그래서 해당 코드가 바로 호출 되는 것이 아니라 스프링 컨테이너를 통해서 스프링 빈을 찾아오게 됩니다. 여기서는 @Bean memberRepository에서 생성한 빈을 찾아오는 것입니다.

이렇게 해서 결과적으로 MemoryMemberRepository는 딱 1개만 생성되어 사용됩니다.

@Configuration이 있고 없고의 차이는 바로 다음 강의인 @Configuration과 바이트코드 조작의 마법에서 이해할 수 있습니다.

감사합니다.

심모님의 프로필 이미지
심모

작성한 질문수

질문하기