2023. 6. 20. 15:37ㆍHigh 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의 AdvisedSupport
→ CglibAopProxy
→ DynamicAdvisedInterceptor
흐름을 타고 들어가면 결국 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 |