inflearn logo
강의

講義

知識共有

スプリングコア原理 - 上級編

SpringBoot 4.0.6 버전에서 PackageLogTracePostProcessor exception

23

tonem1236794

投稿した質問数 1

0

학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.

1. 강의 내용과 관련된 질문을 남겨주세요.
2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.
(자주 하는 질문 링크: https://bit.ly/3fX6ygx)
3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.
(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)

질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.
=========================================
[질문 템플릿]
1. 강의 내용과 관련된 질문인가요? 예
2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예
3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예

[질문 내용]
안녕하세요 영한님. 좋은 강의 만들어주셔서 감사합니다.

다름이 아니라, 제가 "77강 빈 후처리기 - 적용" 강의 내용을 따라가면서 겪은 exception 에 대해 공유드리고자 글 작성합니다.

@Slf4j
public class PackageLogTracePostProcessor implements BeanPostProcessor {

    private final String basePackage;
    private final Advisor advisor;

    public PackageLogTracePostProcessor(String basePackage, Advisor advisor) {
        this.basePackage = basePackage;
        this.advisor = advisor;
    }

    @Override
    public @Nullable Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        log.info("param beanName={} bean={}", beanName, bean.getClass());

        // 프록시 대상 여부 체크
        // 프록시 적용 대상이 아니면 원본 그대로 진행
        String packageName = bean.getClass().getPackageName();
        if (!packageName.startsWith(basePackage)) {
            return bean;
        }

        // 프록시 대상이면 프록시를 만들어서 반환
        ProxyFactory proxyFactory = new ProxyFactory(bean);
        proxyFactory.addAdvisor(advisor);

        Object proxy = proxyFactory.getProxy();
        log.info("create proxy: target={} proxy={}", bean.getClass(), proxy.getClass());
        return proxy;
    }
}

제가 강의 내용을 따라하면서 작성한 PackageLogTracePostProcessor 파일인데, 이 상태로 실행하면 Failed to instantiate [hello.proxy.app.v1.OrderControllerV1]: Illegal factory instance for factory method 'orderControllerV1'; instance: jdk.proxy2.$Proxy56 이라는 내용과 함께 exception 이 발생합니다. 이에 대한 원인은 아래와 같이 파악했습니다.

 

  1. AppV1Config 에 붙어있는 @Configuration 은 proxyBeanMethods 속성이 default 로 true 이며, 이로 인해 Spring Boot 는 AppV1Config 를 감싸는 CGLIB 프록시 객체를 생성합니다. (이하 AppV1Config::CGLIB 객체로 칭하겠습니다.)

  2. 이후 AppV1Config::CGLIB 객체는 PackageLogTracePostProcessor 로 들어오게 되고, ProxyFactory 를 통해 한번 더 프록시 객체로 감싸지게 됩니다.

  3. 이 때, AppV1Config::CGLIB 객체는 EnhancedConfiguration 라는 인터페이스를 구현하는 상태이며, 따라서 JDK Dynamic Proxy 객체로 생성이 됩니다.

  4. **앞선 강의에서 설명해주신 대로, JDK Dynamic Proxy 프록시 클래스는 원본 클래스와는 관련이 없습니다. (동일한 인터페이스만 구현하므로)

  5. 그래서 이후 AppV1Config 내부에 선언된 orderControllerV1 빈 객체를 생성하려 할 때, SimpleInstantiationStrategy.java 의 instantiate 메소드 내부에 있는 if (factoryBean != null && !factoryMethod.getDeclaringClass().isAssignableFrom(factoryBean.getClass())) 조건문에 걸려 BeanInstantiationException 이 발생하는 상황입니다.

 

혹시 제가 파악한 내용에 잘못된 부분이 있을지, 아니라면 이에 대한 적절한 대응 코드가 추가되어야 할 것 같은데, 한번 검토해주시면 감사하겠습니다!

spring 디자인-패턴

回答 2

0

yh

안녕하세요. 마성호님

도움을 드리고 싶지만 질문 내용만으로는 답변을 드리기 어렵습니다.

실제 동작하는 전체 프로젝트를ZIP파일로 압축해서 구글 드라이브로 공유해서 링크를 남겨주세요.

구글 드라이브 업로드 방법은 다음을 참고해주세요.

https://bit.ly/3fX6ygx

 

주의: 업로드시 링크에 있는 권한 문제 꼭 확인해주세요

추가로 다음 내용도 코멘트 부탁드립니다.

1. 문제 영역을 실행할 수 있는 방법

2. 문제가 어떻게 나타나는지에 대한 상세한 설명 (오류 화면, 오류 로그 포함)


링크: 공식 서포터즈

링크: 자주하는 질문

감사합니다.

0

tonem1236794

안녕하세요 영한님. 답글 감사합니다.

구글 드라이브 링크: https://drive.google.com/file/d/18U7f8YlixsGwQPmJe3RgndzxScm9NByI/view?usp=sharing

 

추가로 요청주신 코멘트도 추가합니다.

1. 문제 영역을 실행할 수 있는 방법

=> intellij 에서 프로젝트 실행하시면 바로 확인 가능합니다!

 

2. 문제가 어떻게 나타나는지에 대한 상세한 설명 (오류 화면, 오류 로그 포함)

=> intellij 에서 프로젝트 실행하면 아래와 같은 에러 로그와 함께 실행에 실패합니다.

실패 화면 캡쳐

스크린샷 2026-05-23 오전 11.24.13.png.webp

발생하는 에러 로그

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'orderControllerV1' defined in hello.proxy.app.config.AppV1Config: Failed to instantiate [hello.proxy.app.v1.OrderControllerV1]: Illegal factory instance for factory method 'orderControllerV1'; instance: jdk.proxy2.$Proxy56
	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:657) ~[spring-beans-7.0.7.jar:7.0.7]
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:489) ~[spring-beans-7.0.7.jar:7.0.7]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1362) ~[spring-beans-7.0.7.jar:7.0.7]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1194) ~[spring-beans-7.0.7.jar:7.0.7]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:565) ~[spring-beans-7.0.7.jar:7.0.7]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:525) ~[spring-beans-7.0.7.jar:7.0.7]
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:333) ~[spring-beans-7.0.7.jar:7.0.7]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:371) ~[spring-beans-7.0.7.jar:7.0.7]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:331) ~[spring-beans-7.0.7.jar:7.0.7]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:196) ~[spring-beans-7.0.7.jar:7.0.7]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.instantiateSingleton(DefaultListableBeanFactory.java:1218) ~[spring-beans-7.0.7.jar:7.0.7]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingleton(DefaultListableBeanFactory.java:1184) ~[spring-beans-7.0.7.jar:7.0.7]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1121) ~[spring-beans-7.0.7.jar:7.0.7]
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:994) ~[spring-context-7.0.7.jar:7.0.7]
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:621) ~[spring-context-7.0.7.jar:7.0.7]
	at org.springframework.boot.web.server.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:143) ~[spring-boot-web-server-4.0.6.jar:4.0.6]
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:756) ~[spring-boot-4.0.6.jar:4.0.6]
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:445) ~[spring-boot-4.0.6.jar:4.0.6]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:321) ~[spring-boot-4.0.6.jar:4.0.6]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1365) ~[spring-boot-4.0.6.jar:4.0.6]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1354) ~[spring-boot-4.0.6.jar:4.0.6]
	at hello.proxy.ProxyApplication.main(ProxyApplication.java:23) ~[main/:na]

0

communityai8509

안녕하세요, 인프런 AI 인턴입니다. 마성호님.

질문하신 내용에 대해 이해를 돕기 위해 스프링 부트 환경에서의 프록시 사용과 관련된 사항을 설명드리겠습니다.

지금 발생하는 문제는 @Configuration 클래스가 CGLIB에 의해 프록시가 생성되고, 이를 다시 JDK 동적 프록시로 감쌌을 때 발생하는 것으로 보입니다. 일반적으로 @Configuration 클래스의 Bean 메서드에서 @Bean으로 등록된 각각의 객체는 CGLIB 프록시를 통해 관리됩니다. 따라서, 이 설정 클래스를 다른 프록시로 감싸게 되면 해당 프록시는 원본 클래스와 다른 유형이 됩니다.

그로 인해, 빈을 불러올 때 오류가 발생할수 있는데, 다음은 몇 가지 해결 방법입니다:

해결 방법 1: proxyBeanMethodsfalse로 설정

@Configuration 어노테이션의 proxyBeanMethods 속성을 false로 설정하면 프록시를 없앨 수 있습니다:

@Configuration(proxyBeanMethods = false)
public class AppV1Config {
    // ...
}

이렇게 해주시면 CGLIB 프록시가 생성되지 않아서 문제를 해결할 수 있습니다.

해결 방법 2: 직접 프록시 생성 방식을 수정

PackageLogTracePostProcessor에서 setProxyTargetClass(true)를 설정하여, JDK 동적 프록시가 아니라 CGLIB 기반의 프록시로 생성하도록 할 수 있습니다:

ProxyFactory proxyFactory = new ProxyFactory(bean);
proxyFactory.setProxyTargetClass(true);
proxyFactory.addAdvisor(advisor);

이렇게 하면 Spring 설정 클래스가 JDK 동적 프록시 방식으로 전환되지 않습니다.

관련 답변

이와 비슷한 문제를 다룬 다른 질문과 답변을 참고하시면 이해에 도움이 될 것입니다.

이 두 응답은 각각 프록시 문제를 다루고 있는 이전 강의 자료로, 지금 겪고 계신 문제의 해결 방법을 시사합니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

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

0

59

1

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

0

90

2

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

0

63

1

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

0

57

1

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

0

57

1

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

0

79

1

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

1

391

2

final 키워드 사용 관련 질문

0

80

1

안녕하세요

0

70

1

ThradLocal 실무 사례

0

100

2

실무에서의 동시성 문제

0

67

1

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

0

54

1

ProceedingJoinPoint와 MethodInvocation에 대하여

0

71

1

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

0

73

1

안녕하세요 질문있습니다

0

45

1

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

0

40

1

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

0

78

1

순환참조

0

163

2

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

0

80

1

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

0

82

1

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

0

93

1

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

0

118

2

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

0

72

1

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

0

75

2