CGLib MethodInterceptor

2023. 6. 20. 15:37High Level Programming Language/Reflection

🧠 CGLIB MethodInterceptor 완벽 정복

런타임 메서드 제어의 진심, 인터셉터의 모든 것

 

✅ 들어가며

Spring AOP, 프록시 패턴, 동적 코드 삽입을 논할 때 CGLIB은 자주 언급되는 핵심 라이브러리입니다. 그 중심에 있는 인터페이스가 바로 net.sf.cglib.proxy.MethodInterceptor입니다.

이 글에서는 MethodInterceptor의 구조와 역할, 그리고 내부 동작 원리를 바이트코드 수준까지 파고들어 설명합니다.

📌 정의: MethodInterceptor란?

@FunctionalInterface
public interface MethodInterceptor extends Callback {
    Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable;
}

 

🔍 핵심 개념

  • CGLIB 프록시에서 메서드 호출을 가로채기 위한 인터페이스
  • JDK의 InvocationHandler와 유사하지만, 인터페이스 기반이 아니라 클래스 상속 기반
  • Spring AOP에서 @Aspect 없이도 AOP 유사 기능을 구현할 수 있음

🧩 메서드 시그니처 분석

Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable;

 

파라미터 설명
obj 프록시 객체 자신 (this)
method 리플렉션 기반 Method 객체 (타겟 메서드에 대한 리플렉션)
args 메서드 호출 시 전달된 아규먼트 배열
proxy CGLIB의 MethodProxy 객체. invokeSuper를 통해 타겟 메서드 호출 가능
리턴값 타겟 메서드 호출 결과 (또는 조작된 결과)

 

🔧 기본 사용 예제

public class TimeLoggingInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        long start = System.nanoTime();
        Object result = proxy.invokeSuper(obj, args); // 원본 메서드 호출
        long end = System.nanoTime();
        System.out.println(method.getName() + " 실행 시간: " + (end - start) + "ns");
        return result;
    }
}

이렇게 작성된 인터셉터는 모든 메서드 호출 전/후에 시간 측정 로직을 삽입합니다.

 

🔥 proxy.invokeSuper(...) vs method.invoke(...)

메서드 차이점
proxy.invokeSuper(obj, args) 바이트코드 최적화된 CGLIB 방식. 빠르고 안전
method.invoke(obj, args) 자바 리플렉션 기반. 느리고 위험(무한 루프 유발 가능성)

method.invoke(obj, args)는 프록시 객체의 메서드를 다시 호출하기 때문에 intercept()가 또 호출되는 무한 루프에 빠질 수 있습니다.

 

🧠 내부 동작 구조 (프록시 클래스 자동 생성)

 

▶ 자동 생성 클래스 예시 (MyService$$EnhancerByCGLIB$$...)

CGLIB은 ASM을 사용하여 다음과 같은 클래스를 동적으로 생성합니다:

public class MyService$$EnhancerByCGLIB$$a1b2c3 extends MyService {
    public void doSomething() {
        interceptor.intercept(this, method_doSomething, new Object[0], methodProxy_doSomething);
    }
}

이처럼 타겟의 메서드는 프록시 클래스에서 직접 intercept() 호출로 감싸집니다.

 

🔒 제약 사항 및 주의점

항목 설명
final 클래스 ❌ CGLIB은 상속 기반 → final 클래스는 프록시 생성 불가
final 메서드 ❌ 오버라이딩 불가 → intercept() 대상이 아님
기본 생성자 필요 프록시 객체 생성을 위해 무조건 있어야 함
Java 9+ 모듈 제한 --add-opens java.base/java.lang=ALL-UNNAMED 필요

 

🧩 Spring AOP과의 연결

Spring AOP에서 proxyTargetClass = true 설정 시, 내부적으로 CGLIB + MethodInterceptor가 동작합니다.

@EnableAspectJAutoProxy(proxyTargetClass = true)

Spring의 AdvisedSupportCglibAopProxyDynamicAdvisedInterceptor 흐름을 타고 들어가면 결국 CGLIB MethodInterceptor가 호출됩니다.

 

🧪 고급 실습 예: 실행 차단 프록시

public class BlockCertainMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        if (method.getName().equals("deleteUser")) {
            throw new SecurityException("삭제 금지");
        }
        return proxy.invokeSuper(obj, args);
    }
}
  • deleteUser() 메서드만 실행을 막고, 나머지는 통과시킵니다.
  • AOP의 @Before + @Around 를 구현한 것과 같은 효과입니다.

 

✅ 요약

항목 설명
역할 CGLIB 프록시 메서드 호출을 가로채는 인터페이스
핵심 메서드 intercept(Object, Method, Object[], MethodProxy)
리턴값 원래 메서드 결과 or 변경된 결과
특징 고성능, 클래스 기반 프록시, 바이트코드 생성 기반
Spring과 연동 Spring AOP의 core 구성요소 (DynamicAdvisedInterceptor 내부에서 동작)

 

📚 결론

MethodInterceptor는 단순한 인터페이스 같지만, 실제로는 AOP, 트랜잭션 처리, 로깅, 인증, 조건부 차단 등 다양한 기능을 동적으로 주입할 수 있게 해주는 핵심 도구입니다.

이를 이해하면 Spring AOP 내부가 어떻게 동작하는지, 그리고 프록시 기반 프레임워크가 어떻게 설계되는지에 대한 실마리를 얻을 수 있습니다.

 

GitHub: https://github.com/nomadinsunda/JavaLabs/tree/main/src/com/intheeast/cglibdemo

 

JavaLabs/src/com/intheeast/cglibdemo at main · nomadinsunda/JavaLabs

Contribute to nomadinsunda/JavaLabs development by creating an account on GitHub.

github.com

 

'High Level Programming Language > Reflection' 카테고리의 다른 글

Lesson: Members[Fields]  (0) 2024.07.06
Lesson: Classes  (0) 2024.05.27
CGLIB Enhancer  (0) 2023.05.19
Proxy Target Class 조건  (0) 2023.05.17
sealed interface  (0) 2023.05.08