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

HHS님의 프로필 이미지
HHS

작성한 질문수

스프링 핵심 원리 - 기본편

@Configuration과 바이트코드 조작의 마법

@Configuration 없이 @Bean만 사용할 때에 대한 질문

작성

·

1.4K

10

안녕하세요! 진짜 영한님 강의는 최고네요. 감사합니다.

듣다가 질문이 생겼는데요.

`@Configuration`을 주석처리하고 `@Bean`만 남겼을 때 빈으로 등록되긴 하는데 여러개가 등록(싱글톤 X)된다고 하셨잖아요.

 

여기서 헷갈리는 이유는,

"Bean은 스프링컨테이너에서 관리되는 싱글톤 객체이다."라는 이론적 내용과, "같은 이름의 Bean이 여러개 생성되기도 한다"라는 이 강의의 실험 결과과 충돌해서인데요.

엄밀한 정리를 하고 싶어서 질문드려요. 일단은 "같은 이름의 Bean이 여러개 생성되기도 한다"가 먼저 가능성 측면에서 맞는 얘기고, "Bean은 스프링컨테이너에서 관리되는 싱글톤 객체이다."는 정상적(일반적)인 방식으로 Bean을 등록한다면이라는 전제가 깔렸을 때 맞는 얘기일 거로 생각 됩니다.

그럼, 아래 내용중에는 무엇이 맞는 걸까요?

영한님이 보여주신 실험처럼, `@Configuration`을 누락하면 동일한 Bean이 여러번 생성되는 경우가 있는데 이 때,

1. `memberRepository`라는 메서드가 3번이나 호출되었는데, 새로 생성될 때마다 기존에 먼저 생성되었던 빈을 덮어쓰기(override)한다. 고로 생성만 N번 될 뿐이지, 결과적으로는 스프링 내에서는 싱글톤으로 존재한다.(* 만일 이게 맞다면 컴파일타임/런타임 모두 에러가 안나고, 동작 자체에는 문제가 없겠네요. 리소스는 많이 잡아먹겠지만요.)

2. `memberRepository`라는 메서드가 3번이나 호출되어 총 3개의 인스턴스가 빈으로 각각 등록되었다. 고로 이름을 같지만 3개의 빈이 실제로 모두 존재한다. (* 만일 이게 맞다면, 이 경우 빈을 사용하기 위해 주입할 때 컴포넌트 스캔 결과, ConflictingBeanDefinitionException이 뜨게 되겠네요.)

 

---

앗. 질문이 잘못된 부분이 있어서 수정했습니다.

답변 3

23

안녕하세요. Hyunsang Han님, 공식 서포터즈 David입니다.

.
1. 덮어쓰지 않습니다. 생성은 N번 일어납니다.

만약 memberRepository()가 3번 호출되었다면 스프링 빈으로 등록되는 것 1개, 일반 객체로 존재하는 것 2개가 됩니다.

.

왜냐하면 사용자가 작성한 설정정보를 바탕으로 스프링 컨테이너가 빈으로 등록될 대상을 찾고 등록하는 과정은 다음과 같기 때문입니다.

.

1. 설정정보를 읽어서 @Bean이 붙은 메서드 목록을 저장합니다.

2. 메서드 목록을 하나씩 실행하며 반환되는 객체를 빈으로 등록합니다.

예를 들어 위와 같은 클래스가 있다면 다음과 같이 Component1 타입을 가지는 스프링 빈 1개, 일반 객체 2개가 생성됩니다.

1. BeanMethodMap에 component2(), component3(), component1()이 담깁니다.

2. BeanMethodMap에서 하나씩 Method 이름을 꺼내와서 실행합니다.

    2-1. 첫번째 BeanMethod는 component2()입니다. component2()를 실행합니다.

    2-2. new Component2(component1())에서 component1()의 반환값으로 Component1 객체가 넘어옵니다. (정확히는 객체의 참조값이 넘어오지만 편의상 객체라고 하겠습니다.)

    2-3. 2-2에서 생성된 Component1 객체는 스프링 빈으로 등록되지 않습니다. 반환된 Component1 객체는 Component2의 생성자 파라미터로 넘겨져 Component2 객체가 생성됩니다.

    2-4. Component2 객체는 스프링 빈으로 등록됩니다.

2 ~ 2-4번 절차를 반복하면서 다음 순서대로 Component3 객체, Component1 객체가 스프링 빈이 등록됩니다. 

이 과정에서 component2(), component3() 내에서 호출되는 component1()에서 스프링 빈이 아닌 Component1 객체 2개가 생성되고, BeanMehtodMap에서 component1()을 꺼내서 component1()이 실행될 때 생성된 Component1 객체는 스프링 빈으로 등록됩니다.

즉, @Configuration이 없을 때에는 @Bean이 붙은 메소드 내 의존관계로 주입되는 객체가 스프링 빈으로 등록되는 게 아닙니다.

.

만약 @Configuration을 붙였다면 2-2과정에서 new Component2(component1())에서 component1()이 호출될 때 스프링 빈으로 등록하는 과정이 바로 시작됩니다. 그리고 빈을 등록하는 과정에서 동일한 이름으로 싱글톤 빈이 존재하는지 확인합니다. 이렇게 동작하는 이유는 @Configuration을 붙인 Config클래스는 실제 Config타입의 객체가 아닌 Config 타입을 상속하는 SpringCGLIB 객체이기 때문입니다. 따라서 @Configuration을 붙인 Config클래스에 @Bean이 붙은 메서드가 실행될 때는 필요한 의존관계가 있다면 빈이 존재하는지 찾고 없으면 생성하고, 있다면 가져와서 주입해줍니다. 그렇기 때문에 Component1 타입의 스프링 빈이 1개로 유지될 수 있는 것입니다.

.

2. 1번에서 말씀드린대로 빈이 3개씩 존재하지 않습니다. 빈은 1개, 일반 객체 2개의 상태로 존재하게 됩니다.
.
감사합니다.

@Configuration 어노테이션에 대해 궁금했던게 해소됐습니다 감사합니다

설명 짱짱입니다요

설명 지렸다;; 인프런 개발자 분들 천재들만 있는 듯 ;^;

이게 궁금해서 코드로 테스트하고 확인차 질문하려고 했었는데 이런 자세한 답변이 이미 있었네요.. 감사합니다.

0

 같은 이름의 Bean이 여러개 생성될 수 있나요?

안녕하세요. 김형민님, 공식 서포터즈 David입니다.

생성될 수 없습니다.

빈 이름 충돌 예외가 발생합니다.

 

p.s. 질문은 새로운 질문 글로 올려주시면 더욱 빠르게 답변드릴 수 있으니 참고 부탁드립니다.

감사합니다.

0

HHS님의 프로필 이미지
HHS
질문자

자답입니다.

 

1. 메서드가 처음 호출되었을 때, @Bean 어노테이션으로 인해 빈으로 등록되고

2. 메서드가 두번째, 세번째 호출되었을 때에는 똑같은 객체가 이미 빈으로 등록되어있는 상태라서, 그냥 객체 생성에서 끝나버릴 뿐 Bean으로 등록이 되진 않는다.

 

일 것 같은데 맞는지 답변 주시면 감사하겠습니다!

HHS님의 프로필 이미지
HHS

작성한 질문수

질문하기