작성
·
140
·
수정됨
0
06:03 부터 @SpringBootApplication
어노테이션으로 스프링구동을 진행함으로서
앞서 AutoAppConfigTest 를 했을 때 replace되어 정상구동되는 현상과 반대되는 결과를 보여주시고 계십니다.
스프링부트는 오류를 발생시키는 것을 저도 똑같이 확인하기 위해 실습을 했는데요. 결과메세지가 달랐습니다.
Parameter 0 of constructor in hello.core.member.MemberServiceImpl required a single bean, but 2 were found:
- memoryMemberRepository: defined in file [C:\Users\eh\Desktop\Source\core\out\production\classes\hello\core\member\MemoryMemberRepository.class]
- memberRepository: defined by method 'memberRepository' in class path resource [hello/core/AppConfig.class]
강의에서는 AutoAppConfig에 작성한 수동빈과 @Compoent로 등록된 자동빈의 충돌을 다뤘으나, 저의 경우 AppConfig가 등장했습니다.
강사님도 AppConfig를 건들지 않으신것으로 알고있습니다. 다음과 같습니다.
@Configuration
public class AppConfig {
public AppConfig() {
System.out.println("AppConfig 생성자호출");
}
// 서비스단
@Bean
public MemberService memberService(){
System.out.println("call AppConfig.memberService");
return new MemberServiceImpl(memberRepository());
}
@Bean
public OrderService orderService(){
System.out.println("call AppConfig.orderService");
return new OrderServiceImpl(
memberRepository(),
discountPolicy());
}
// Repository
@Bean
public MemberRepository memberRepository(){
System.out.println("call AppConfig.memberRepository");
return new MemoryMemberRepository();
}
@Bean
public DiscountPolicy discountPolicy(){
System.out.println("call AppConfig.discountPolicy");
return new RateDiscountPolicy();//return new FixDiscountPolicy();
}
}
다음은 자동구성입니다.
@ComponentScan(
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {
public AutoAppConfig() {
System.out.println("AutoAppConfig의 생성자호출");
}
// [수동빈등록]
// 자동등록 된 빈과 충돌이 발생하지 않으며 아래 수동등록 빈으로 덮어써짐
@Bean(name = "memoryMemberRepository")
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
@Component
public class MemoryMemberRepository implements MemberRepository{...}
총 3개의 코드가 충돌을 일으킬 후보를 추려봤습니다.
AppConfig의 MemberRepository 수동등록 코드(name = "memberRepository")
AutoAppConfig의 MemberRepository 수동등록 코드(name = "memoryMemberRepository")
MemoryMemberRepository 자동등록 코드(name = "memoryMemberRepository")
강사님은 2,3이 충돌했는데
저는 왜 1,3이 충돌한 걸까요ㅠㅠ
답변 2
1
@y2gcoder 님 질문 하나만 더 드리고 싶습니다.
강의 7:00 에 spring.main.allow-bean-definition-overriding=true
을 application.prorperties에 명시를 하면 에러가 발생되지 않음을 보여주셨는데요.
이 속성을 적용하기전
AppConfig의 MemberRepository 수동빈(name = "memberRepository")
AutoAppConfig의 MemberRepository 수동빈(name = "memoryMemberRepository")
MemoryMemberRepository 자동빈(name = "memoryMemberRepository")
속성을 적용하면 다음과 같은 결과가 그려집니다.
AppConfig의 MemberRepository 수동빈(name = "memberRepository")
AutoAppConfig의 MemberRepository 수동빈(name = "memoryMemberRepository") --> 오버라이딩으로 수동이 살아남음
MemoryMemberRepository 자동빈(name = "memoryMemberRepository")
--> 오버라이딩으로 자동빈은 사라짐
위 결과로 인해 제가 진행한 실습은 MemberServiceImpl 생성오류가 발생합니다. 위 1, 2가 MemberServiceImpl의 의존성 후보로서 충돌하기 때문입니다.
강의를 보면 강사님은 1, 2가 충돌하지 않으셨는데요. 어떤 이유인지 알 수 있을까요?
이런 경우 spring.main.allow-bean-definition-overriding=true
설정을 적용하는 이유는, 동일한 이름의 빈이 여러 개 등록될 때 우선순위를 명시적으로 정하기 위해서입니다. 예를 들어, 수동 빈이 자동 빈을 오버라이드하게 하려는 의도라면 이 설정이 필요하지만, 빈 이름이 서로 다르면 빈 충돌은 발생하지 않습니다 🙂
0
안녕하세요, 인프런 AI 인턴입니다. 창신동 장첸님께서 스프링 강의를 진행하시면서 발생한 문제에 대해 살펴보겠습니다. 현재 겪고 계신 문제는 “빈 충돌”과 관련된 내용입니다. 여러분께서 주신 코드를 보면, AppConfig
와 AutoAppConfig
에서 모두 MemoryMemberRepository
라는 동일한 유형의 빈을 등록하고 있어서 발생하는 문제로 보입니다.
스프링에서는 수동으로 등록한 빈이 자동으로 등록된 빈을 덮어쓰는 것이 일반적입니다. 그러나 현재 에러 메시지를 보면 두 개의 빈이 동시에 존재하여, 어떤 빈을 주입해야 할지 스프링이 알 수 없어서 문제가 발생하고 있습니다.
빈의 이름을 명시적으로 지정: AutoAppConfig
의 수동 등록 빈에 이름을 명시적으로 주는 것도 하나의 방법입니다. 이미 시도하신 것처럼 @Bean(name = "memoryMemberRepository")
를 사용하여 이름 충돌을 피할 수 있습니다.
자동 빈 등록을 제외: ComponentScan
을 하실 때, MemoryMemberRepository
가 직접 스캔되지 않도록 조정할 수 있습니다. 즉, @Component
대신 다른 등록 방법을 사용하는 것이 좋습니다.
구성 클래스 정리: 만약 AppConfig
와 AutoAppConfig
둘 중 하나만 사용하도록 코드를 변경할 수 있다면, 중복 빈 등록을 예방할 수 있습니다. 구성 클래스를 한 곳으로 통합하여 모든 빈들이 확실하게 한 곳에서 관리되도록 하는 것도 좋은 방법입니다.
관련된 질문과 비슷한 해결책들을 살펴보시려면 아래 링크를 참고하시기 바랍니다:
- 스프링 빈 중복 등록 해결 방법 : 이 링크에서는 스프링 빈 충돌 문제와 관련한 예시와 해결책을 설명하고 있습니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다. 현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.
아이고....
강의자료에 @Configuration이 윗장에 짤려서 밑에부분만 복붙됐었네요.
정말 감사드립니다. 스프링부트3버전 이슈와 관계없이 Build and Run을 intellij 로 설정해도
강사님꼐서 진행한 결과 그대로 확인할 수 있게됐습니다.
정리하자면, @Configuration이 없었기 때문에
AutoConfig 빈객체가 만들어지지 않았고 그 안에 정의된 @ComponentScan과 수동빈 정의들이 아무쓸모 없어졌던 것입니다.
그리고 1,3 충돌할 수 있었던 이유는
MemberServiceImpl에 필요한 의존성은 타입을 기반으로 찾기 때문에
1,3의 경우처럼 아무리 빈이름이 다르더라도 ctx.getBean(클래스명) 처럼 클래스와 그 자식클래스가 조회되기 때문에 뭘 골라쓸지 몰라 충돌이 발생했던 것입니다.
다시 한번 감사드립니다.