[3/3] Spring과 싱글톤 패턴 - @Configuration의 비밀
개요
스프링은 스프링 컨테이너에 등록된 빈이 호출되면 해당 인스턴스 자체를 주는 것이 아니라 스프링에서 특별하게 만든 인스턴스를 반환한다. 이 인스턴스는 기존의 인스턴스를 상속받아 만들어졌으며 getClass를 통해 참조하고있는 클래스를 살펴보면 CGLIB라는 단어가 있는 것을 알 수 있다. 이것이 스프링이 만든 가상의 클래스라는 의미이다.
new가 여러번 호출되는데 싱글톤이 유지된다?
AppConfig예제를 살펴보자.
@Configuration
public class AppConfig {
@Bean
public MemberServiceImpl memberService() {
System.out.println("call memberService"); // 로그
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
System.out.println("call memberRepository"); // 로그
return new MemberRepository();
}
}
MemberRepository와 MemberService가 빈으로 등록되어 있다. MeberService는 빈으로 등록될 때 MemberRepository를 호출해서 파라미터로 가진 채 등록되고 있다.
코드를 잘 살펴보면 뭔가 이상한 것을 알 수 있다.
memberRepository()에서 new MemberRepositroy를 하고있고 memberService()에서도 memberRepository()를 호출해서 파라미터로 넘기고 있다. 이렇게 되면 MemberRepository가 2개가 생성되기 때문에 싱글톤이 깨지게 될 것이다. 실제로도 그런지 한 번 테스트코드로 확인해보자.
AppConfig에는 각 빈이 호출될 때마다 어떤 빈이 호출됐는지 로그를 찍을 수 있게 해놨다. 만약 이 상태에서 AppConfig를 스프링 컨테이너에 등록해서 사용한다면
- memberRepository()가 실행되면서 로그 한번
- memberService()가 실행되면서 로그 한번
- memberService()내부에서 호출하는 memberRepository()가 실행되면서 로그 한번
총 3개의 로그가 찍혀야 할 것이다.
이제 테스트코드로 실험해보자
@Test
void Configuration_테스트() throws Exception {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
AppConfig appConfig = ac.getBean(AppConfig.class);
System.out.println("appConfig = " + appConfig.getClass());
}
AppConfig를 빈으로 등록하면서 내부에 빈들도 모두 등록되었을 것이다. 그리고 그 과정에서 로그가 찍혔는데 결과는 아래와 같다.
위에서 계산했던대로라면 3개의 로그가 찍혀야 하지만 지금은 로그가 2개만 찍힌 것을 볼 수 있다. 아직은 이유를 잘 모르겠지만 놀랍게도 스프링은 이러한 코드에도 싱글톤을 유지하고 있는 것이다.
그리고 테스트에서 getClass()를 사용해서 확인한 appConfig가 참조하는 주소를 보면 단순히 AppConfig가 아니라 뒤에 의미를 알 수 없는 문자들이 붙어있다. 그 중에는 CGLIB라는 문자가 있다. 이것은 AppConfig가 빈으로 등록되면서 이 인스턴스를 상속받은, 스프링이 임의로 만든 AppConfig라는 의미이다.
분명 중복되는 new가 있었음에도 싱글톤이 꺠지지않고 유지되는 것은 스프링내부에서 빈으로 등록된 객체를 생성할 때 이미 빈에 등록이 되있으면 빈에 등록된 인스턴스를 반환하고 없다면 새로 만들고 빈에 등록하도록 동작하고 있기 때문이다. 그리고 이 동작을 가능하게 하는 것이 바로 @Configuration어노테이션이다.
지금 AppConfig에는 @Configuration이 등록되어 있는데 이 어노테이션이 없어도 내부에 선언된 빈들은 모두 정상적으로 스프링 컨테이너에 등록이 된다. 하지만 new를 여러번 실행해서 객체를 만드는 코드에서는 싱글톤이 유지되지 못하고 계속해서 새로운 객체들이 생성된다. @Configuration을 지우고 AppConfig를 테스트 해보면 아래와 같은 결과가 나타나게 된다.
우리가 코드만 보고 예상했던대로 로그가 총 3개가 찍혔다. memberRepository가 2개가 있으므로 이 2개는 서로 다른 주소값을 가지게 될 것이다.
그리고 getClass를 통해 찍은 appConfig의 참조값도 정확하게 AppConfig를 참조하고 있음을 볼 수 있다. @Configuration이 제외됐기 때문에 스프링이 가상의 다른 AppConfig를 생성하지 않고 원래의 AppConfig를 그대로 사용하는 것이다. 그렇기 때문에 memberRepository가 여러번 생성되면서 싱글톤이 유지되지 못하고 있다.
이렇게 스프링은 내부적으로 많은 동작들을 하며 기본적으로 싱글톤을 유지하고 있다.
About
해당 게시글은 김영한님의 '스프링 핵심 원리 - 기본편'을 공부하고 작성한 내용입니다.
해당 게시글의 소스코드는 https://github.com/ssung0810/spring_study/tree/main/singleton에서 볼 수 있습니다.
'Spring > Core' 카테고리의 다른 글
스프링과 데이터베이스[1] - JDBC와 커넥션 풀 (0) | 2022.11.08 |
---|---|
스프링 주입의 다양한 방법들 (0) | 2022.06.13 |
스프링 빈 등록의 다양한 방법들 (0) | 2022.06.09 |
[2/3] Spring과 싱글톤 패턴 - 싱글톤 패턴의 주의점 (0) | 2022.06.06 |
[1/3] Spring과 싱글톤 패턴 (0) | 2022.06.03 |
댓글
이 글 공유하기
다른 글
-
스프링 주입의 다양한 방법들
스프링 주입의 다양한 방법들
2022.06.13 -
스프링 빈 등록의 다양한 방법들
스프링 빈 등록의 다양한 방법들
2022.06.09 -
[2/3] Spring과 싱글톤 패턴 - 싱글톤 패턴의 주의점
[2/3] Spring과 싱글톤 패턴 - 싱글톤 패턴의 주의점
2022.06.06 -
[1/3] Spring과 싱글톤 패턴
[1/3] Spring과 싱글톤 패턴
2022.06.03