Spring/김영한 스프링 핵심원리 - 기본편

[섹션6-4] 중복 등록과 충돌

보름달빵 2024. 2. 25. 17:57

 

컴포넌트 스캔을 하는데 같은 이름의 빈을 등록하면 어떻게 될까? 

 

이런 경우는 두가지 경우가 있다.

  1. 컴포넌트 스캔 vs 컴포넌트 스캔  : 충돌하는 빈들이 모두 컴포넌트 스캔 대상인 경우 
  2. 수동 빈 등록 vs 컴포넌트 스캔 :  컴포넌트 스캔한 빈과 직접 등록해준 빈의 이름이 같은 경우 

 

 

1.  컴포넌트 스캔 vs 컴포넌트 스캔 

: 컴포넌트 스캔으로 등록했는데 같은 이름의 빈을 또다시 컴포넌트 스캔으로 등록하려는 경우 

 

@Component("service")
public class MemberServiceImpl implements MemberService {

 

@Component("service")
public class OrderServiceImpl implements OrderService{

 

두 클래스를 service라는 같은 이름의 빈으로 설정하고 컴포넌트 스캔을 할 경우, 

ConflictingBeanDefinitionException 예외가 발생한다. 

 

 

2.  수동 빈 등록 vs 컴포넌트 스캔

: 설정 클래스에서 직접 등록한 빈의 이름과, 컴포넌트의 스캔 대상중에 동일한 이름이 있는 경우

 

 

@Component // memoryMemberRepository로 등록됨
public class MemoryMemberRepository implements MemberRepository{
public class AutoAppConfig {

    @Bean(name="memoryMemberRepository")
    public MemberRepository memberRepository(){
        return new MemoryMemberRepository(); 
    }
    
}

 

MemoryMemberRepository의 경우 이미 @Component 이므로 memoryMemberRepository라는 이름으로 컨테이너에 등록될 것이다. 

그런데 만약 설정 클래스에서 똑같은 이름의 빈을 등록해주면 어떻게 될까? 

 

 

public class AutoAppConfigTest {

    @Test
    @DisplayName("컴포넌트 스캔해서 스프링 빈 등록하기")
    void basicScan(){

        AnnotationConfigApplicationContext ac =
                new AnnotationConfigApplicationContext(AutoAppConfig.class);

        MemberService memberService = ac.getBean(MemberService.class);
        assertThat(memberService).isInstanceOf(MemberService.class);

    }
}

 

 

 

테스트 실행 결과 테스트가 성공했다. 

분명히 같은 이름의 빈이 두개 인데 왜 테스트에 성공한 것일까? 

 

로그를 읽어보면 이런 문구가 나온다. 

 Overriding bean definition for bean 'memoryMemberRepository' with a different definition: 
 replacing[Generic bean: class [hello.core.member.MemoryMemberRepository]

 

즉, 수동 빈이 등록 우선권을 가진다.

( 수동빈이 자동빈을 오버라이딩 했다) 

 

물론 개발자가 의도적으로 이런 결과를 기대했다면, 자동 보다는 수동이 우선권을 가지는 것이 좋다. 하지만 현실은 개 발자가 의도적으로 설정해서 이런 결과가 만들어지기 보다는 여러 설정들이 꼬여서 이런 결과가 만들어지는 경우가 대 부분이다! 그러면 정말 잡기 어려운 버그가 만들어진다. 항상 잡기 어려운 버그는 애매한 버그다.

 

 

그래서 최근 스프링 부트에서는 수동 빈 등록과 자동 빈 등록이 충돌나면 오류가 발생하도록 기본 값을 바꾸었다.

 

 

 

2-1. 수동 빈 등록, 자동 빈 등록 오류시 스프링 부트 에러

스프링 부트인 CoreApplication을 실행해보면 오류를 볼 수 있다.

 

***************************
APPLICATION FAILED TO START
***************************

Description:

The bean 'memoryMemberRepository', defined in class path resource [hello/core/AutoAppConfig.class], could not be registered.
A bean with that name has already been defined in file [...\hello\core\member\MemoryMemberRepository.class]
and overriding is disabled.

Action:

Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

 

스프링 부트는 현재 오버라이딩이 불가하기 때문에 빈을 등록할 수 없다고 한다. 

 

따라서 오버라이딩 기능을 사용하고 싶다면 아래 코드를 추가하라고 한다. 

 

 spring.main.allow-bean-definition-overriding=true

 

 

이 코드를 application.properties에 추가하고 스프링 부트를 재실행 한 결과 

 

Overriding bean definition for bean 'memoryMemberRepository' with a different definition: 
replacing [Generic bean: class [hello.core.member.MemoryMemberRepository]

 

오버라이딩이 성공했음을 알 수 있다.