이 시리즈는 인프런 강의(김영한 님의 ‘스프링 핵심 원리 - 기본편’)로 공부하며 혼자 기록하고, 사람들과도 공유할 수 있도록 작성하는 글이다. 최대한 추가적인 정보는 공식 홈페이지, 문서를 보며 얻을 예정이다.
(개인적인 생각과 이해가 들어가 있기 때문에 저의 ‘무식함’이 있을 수 있습니다😜 혹시라도 이 글을 보게 되시는 분이 계시다면 잘못된 부분 댓글로 많이 알려주시면 너무 감사하겠습니다!!)
GitHub Repository : https://github.com/jcw1031/spring-core-study
@Configuration
어노테이션의 역할에 대해 알아보자.
AppConfig
클래스의 코드를 보면 memberService()
가 호출되면 memberRepository()
도 호출되어 new MemoryMemberRepository()
가 호출된다. 또 orderService()
가 호출되면 memberRepository()
도 호출되어 한 번 더 new MemoryMemberRepository()
가 호출된다.
코드로만 보면 MemoryMemberRepository
가 두 개가 생성되어 싱글톤을 보장하지 못할 것처럼 보인다. 테스트를 통해 싱글톤 빈인지 확인해 보자.
테스트를 위해 MemberServiceImpl
과 OrderServiceImpl
두 개 모두 주입된 MemberRepository
를 반환하는 메서드를 추가한다.
test
패키지의 singleton
패키지 내에 ConfigurationSingletonTest
테스트 클래스를 만든다. 스프링 컨테이너를 생성하고, 테스트 코드에서 MemberServiceImpl
빈과 OrderServiceImpl
빈을 조회한다.
빈을 조회할 때는 구체가 아닌 역할로 조회하는 게 좋지만, 역할이 아닌 구체 클래스로 조회한 이유는 위에서 생성한
getMemberRepository()
메서드를 사용하기 위함이다.
memberService
와 orderService
각각의 MemberRepository
를 조회하고, 이 둘이 같은지 확인해 보자. 테스트를 돌려보면 성공한다. 싱글톤이 보장된다는 것이다.
그럼 MemberServiceImpl
과 OrderServiceImpl
에 주입된 MemberRepository
가 아닌 빈으로 등록된 MemberRepository
도 조회하여 비교해 보자. 테스트를 실행해 보면 3개의 MemberRepository
모두 동일한 빈임을 알 수 있다.
분명 new MemoryMemberRepository()
가 여러 번 호출되어 여러 개가 생성되었을 것 같지만 모두 똑같은 빈이라는 것을 확인했다. new MemoryMemberRepository()
가 여러 번 호출되는 것이 아닌지 확인해 보자.
간단하게 AppConfig
에 로그를 추가해서 확인해 보자.
soutm
을 입력하고 Tab을 누르면 자동으로 클래스와 메서드 이름을 출력하는 문장이 완성된다. 앞에 call만 추가해 주자.
만약 여러 번 호출된다면 로그는 아래처럼 출력될 것이다.(순서는 보장되지 않는다.)
왜 3번인지 헷갈릴 수 있다.
memberRepository()
에도@Bean
이 사용되었기 때문에 빈으로 등록되는 과정이 있기 때문에 세 번 호출된다고 가정할 수 있다.
/*
..memberService -> ..memberRepository -> ..memberRepository -> ..orderService -> memberRepository
*/
아까 만든 테스트 코드를 그대로 실행해 보면, memberRepository
로그가 세 번 찍힐 것이라 예상한 것과 달리 한 번만 출력된다.
어떻게 스프링 컨테이너는 싱글톤 빈을 보장할까?
비밀은 @Configuration AppConfig
에 있다.
AppConfig
를 출력해 보자. AppConfig
도 빈으로 등록되니 getBean()
을 통해 빈을 조회하고 출력한다.
출력 결과를 보면 그냥 AppConfig
가 아닌 뒤에 무언가 붙어있는 것을 확인할 수 있다. 순수한 클래스라면 bean = class Group이름.core.AppConfig
라고 출력되었을 것이다.
뒤에 ~CGLIB
이 붙어있다. 이것은 우리가 만든 클래스가 아닌, 스프링이 CGLIB이라는 바이트 코드 조작 라이브러리를 사용해 AppConfig
클래스를 상속받은 임의의 다른 클래스를 생성해 이를 스프링 빈으로 등록한 것이다.
우리가 코드의 깊숙한 내부를 알 수는 없지만, 만약
MemoryMemberRepository
가 빈으로 등록되어 있는지 확인한 후에 등록되어 있지 않으면 새로 빈으로 등록하고, 등록되어 있다면 새로 빈을 등록하지 않고 기존 빈을 주입해 주는 형식으로 내부 코드를 조작했을 것이라고 예상해 볼 수 있겠다.
@Configuration
어노테이션을 사용하지 않으면 싱글톤 패턴이 깨지는지 확인해 보자.
AppConfig
의 @Configuration
을 주석 처리한다.
동일하게 테스트를 실행해 보면, 순수한 AppConfig
클래스가 스프링 빈에 등록된다.
configurationTest
를 실행해 보면, memberRepository
가 세 번 호출되고 객체를 확인해 보면 모두 다른 객체임을 알 수 있다.
MemberServiceImpl
과OrderServiceImpl
의private final MemberRepository memberRepository;
에 주입된 것은 스프링 빈이 아니다.private final MemberRepository memberRepository = new MemoryMemberRepository;
와 동일하다. 스프링 컨테이너가 관리하지 않는 객체인 것이다.
스프링 설정 정보는 항상 @Configuration
을 사용하도록 한다. 스프링 컨테이너가 싱글톤 빈을 보장할 수 있었던 비밀이 @Configuration
어노테이션에 있다는 것을 알아보았다. 다음 시간에는 컴포넌트 스캔에 대해 공부한다.