[섹션6-4] 중복 등록과 충돌
컴포넌트 스캔을 하는데 같은 이름의 빈을 등록하면 어떻게 될까?
이런 경우는 두가지 경우가 있다.
- 컴포넌트 스캔 vs 컴포넌트 스캔 : 충돌하는 빈들이 모두 컴포넌트 스캔 대상인 경우
- 수동 빈 등록 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]
오버라이딩이 성공했음을 알 수 있다.