inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

스프링 핵심 원리 - 고급편

구체 클래스 기반 프록시 - 적용

V2 ConcreteProxyController 빈 중복 에러 (SpringBoot V3)

603

iseunghan

작성한 질문수 1

0

[질문 템플릿]
1. 강의 내용과 관련된 질문인가요? (예/아니오) 예
2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 예
3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) 예

[질문 내용]
안녕하세요, 영한님! 먼저 이렇게 좋은 강의를 들을 수 있게 되어서 너무 기쁘고 감사드립니다.

먼저 저는 SpringBoot 3.0.5 버전 사용중입니다.

그렇기 때문에 어쩔 수 없이 @RequestMapping으론 컴포넌트 스캔 대상이 되지 않기 때문에 V2 컨트롤러에는 @RestController가 붙어있습니다.

구체 클래스 기반 프록시 V2에 적용할 때, 프록시 컨트롤러를 빈으로 등록하려고 하니 아래처럼 빈이 중복으로 등록된다고 오류가 발생하였습니다.

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

Description:

The bean 'orderControllerV2', defined in class path resource [me/iseunghan/hello/proxy/app/v2/concrete_proxy/ConcreteProxyConfig.class], could not be registered. A bean with that name has already been defined in file [/Users/iseunghan/workspaces/study/lecture-spring-advanced/proxy/build/classes/java/main/me/iseunghan/hello/proxy/app/v2/OrderControllerV2.class] and overriding is disabled.

Action:

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


Process finished with exit code 1

그래서 로그에 친절하게 나와있는대로 application.yaml에 아래 설정을 추가하니 정상적으로 실행 및 테스트가 되었습니다.

application.yaml

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

혹시 이와 같은 방법 이외에는 다른 방법이 없을지 여쭤보고 싶습니다..! 감사합니다.

spring 디자인-패턴

답변 2

0

iseunghan

안녕하세요, 답변 감사드립니다. 하지만 뭔가 명확한 답변을 못받아 다시 질문 드립니다..

 

일단 제가 생각한 빈이 중복된 이유는 다음과 같습니다.

  1. @RestController가 붙어있는 클래스가 먼저 빈으로 등록

  2. @Configuration 내부에 @Bean으로 등록하려는 빈이 이미 1번에서 등록이 되어 중복 등록에 대한 오류가 생긴 것 입니다.

스프링부트 버전 3 이상에서는 인터페이스에 @RestController를 붙여줘야만 컨트롤러로 인식하여 구현체가 구현했을 때 엔드포인트 매핑이 된다는 것입니다.

현재 강의에서는 인터페이스 기반 프록시를 구현중이라 어쩔수 없이 컨트롤러 코드에 @RestController를 붙여야 하는 상황인데.. @RestController를 제거하면 URLMapping이 정상적으로 이루어지지 않습니다.. 이럴 때에는 어떻게 하면 좋을지 궁금합니다.

 

아래 코드 첨부드립니다.

@RequiredArgsConstructor
@RestController
public class OrderControllerV2 {

    private final OrderServiceV2 orderService;

    @GetMapping("/v2/request")
    public String request(String itemId) {
        orderService.orderItem(itemId);
        return "ok";
    }

    @GetMapping("/v2/no-log")
    public String nolog() {
        return "ok";
    }

}
public class OrderControllerConcreteProxy extends OrderControllerV2 {
    private final OrderControllerV2 target;
    private final LogTrace logTrace;

    public OrderControllerConcreteProxy(OrderControllerV2 target, LogTrace logTrace) {
        super(null);
        this.target = target;
        this.logTrace = logTrace;
    }

    @Override
    public String request(String itemId) {
        TraceStatus status = null;
        try {
            status = logTrace.begin(this.getClass().getSimpleName() + ".save()");
            String response = target.request(itemId);
            logTrace.end(status);
            return response;
        } catch (Exception e) {
            logTrace.exception(status, e);
            throw e;
        }
    }

    @Override
    public String nolog() {
        TraceStatus status = null;
        try {
            status = logTrace.begin(this.getClass().getSimpleName() + ".save()");
            String response = target.nolog();
            logTrace.end(status);
            return response;
        } catch (Exception e) {
            logTrace.exception(status, e);
            throw e;
        }
    }
}
@Configuration
public class ConcreteProxyConfig {

    @Bean
    public OrderRepositoryV2 orderRepositoryV2(LogTrace logTrace) {
        OrderRepositoryV2 orderRepositoryV2 = new OrderRepositoryV2();
        return new OrderRepositoryConcreteProxy(orderRepositoryV2, logTrace);
    }
    @Bean
    public OrderServiceV2 orderServiceV2(LogTrace logTrace) {
        OrderServiceV2 orderServiceV2 = new OrderServiceV2(orderRepositoryV2(logTrace));
        return new OrderServiceConcreteProxy(orderServiceV2, logTrace);
    }

    @Bean
    public OrderControllerV2 orderControllerV2(LogTrace logTrace) {
        OrderControllerV2 orderControllerV2 = new OrderControllerV2(orderServiceV2(logTrace));
        return new OrderControllerConcreteProxy(orderControllerV2, logTrace);
    }
}
@Import(ConcreteProxyConfig.class)
@SpringBootApplication(scanBasePackages = {"me.iseunghan.hello.proxy.app.v2"})
public class ProxyApplication {

    public static void main(String[] args) {
        SpringApplication.run(ProxyApplication.class, args);
    }

    @Bean
    public LogTrace logTrace() {
        return new ThreadLocalLogTrace();
    }
}

패키지 구조

image

0

인프런 AI 인턴

안녕하세요, 인프런 AI 인턴입니다.

중복으로 빈이 등록된 오류가 발생했다는 내용이군요. 이러한 오류는 동일한 이름의 빈이 여러 곳에서 중복으로 등록되었을 때 발생합니다.

오류 메시지를 보면 orderControllerV2라는 이름의 빈이 ConcreteProxyConfig.classOrderControllerV2.class에 중복으로 등록되었다고 나와있습니다.

이러한 경우, 스프링은 이름이 같은 두 개의 빈을 구분하지 못해 오류가 발생하게 됩니다. 이 문제를 해결하기 위해서는 중복으로 등록된 빈 중 하나를 제거하거나, 빈의 이름을 변경하여 충돌을 피해야 합니다.

따라서, ConcreteProxyConfig.class 또는 OrderControllerV2.class 중 하나에서 빈을 제거하거나, 빈의 이름을 변경하실 수 있도록 해주시면 됩니다.

더 자세한 사항을 알고 싶으신 경우, 스프링의 빈 등록 규칙과 중복 등록되는 이유에 대해 공부해보시면 도움이 되실 것입니다.

더 궁금한 사항이 있다면 언제든지 물어보세요. 도움이 될 수 있도록 최선을 다하겠습니다.

어드바이스 순서 디폴트 기준이 궁금합니다.

0

55

1

AspectV1 예제를 @Configuration 수동 등록으로도 가능한가요?

0

81

2

구체 클래스를 상속받아 확장한 형태도 클래스패턴/데코레이터 패턴이라고 칭하나요?

0

57

1

TraceTemplate 을 미리 빈으로 등록해서 사용할때 이렇게 설정하는게 맞는건가요?

0

54

1

Decorator 에서 추상메서드로 뺄때 질문 있습니다.

0

56

1

대상 클래스에 기본 생성자가 없을 때

0

76

1

스프링 부트 버전 4.0 aop 의존성 명칭 변경

1

362

2

final 키워드 사용 관련 질문

0

78

1

안녕하세요

0

65

1

ThradLocal 실무 사례

0

98

2

실무에서의 동시성 문제

0

64

1

로그 등 부가 기능에 대한 테스트코드

0

50

1

ProceedingJoinPoint와 MethodInvocation에 대하여

0

67

1

실무 멀티스레딩에 대한 궁금증

0

70

1

안녕하세요 질문있습니다

0

45

1

AspectV3 aop 실행 순서 문의 드립니다

0

38

1

MDC vs AOP 중에 고민중입니다.

0

76

1

순환참조

0

161

2

this를 단독으로 사용할때랑 파라미터 바인딩 할때 차이

0

78

1

빈 후처리에 등록 질문 있습니다.

0

77

1

AppConfig 설정하는 설명중에 이해가 안되는게 있습니다.

0

90

1

ThreadLocal을 지역변수로 선언하면 remove가 필요할까요?

0

118

2

@Aspect 어노테이션으로 생성된 Advisor의 Bean 저장 여부

0

69

1

ThreadLocal 사용시 부작용이 있을수 있을까요? (ThreadLocal.remove()를 잘한다고 해도..)

0

73

2