JDK 동적 프록시와 CGLIB

parkrootseok·2025년 4월 15일

스프링

목록 보기
10/12
post-thumbnail

들어가며

Spring AOP를 공부하면서 프록시 객체를 접하게 되었습니다. 프록시 객체는 쉽게 말하면, 비서라고 할 수 있습니다. 누군가 요청을 하면, 그에 필요한 부가적인 작업들을 대신 수행하게 됩니다. 이 프록시 객체를 생성하는 방법인 JDK 동적 Proxy 방식과 CGLIB 방식을 살펴보겠습니다.

JDK 동적 프록시

JDK 동적 프록시는 Java 표준 라이브러리에서 제공하는 방식으로 대상이 인터페이스인 경우 사용합니다. Reflection를 활용한 Prxoy클래스를 사용해 프록시 객체를 생성합니다.

Reflection 이란?
Reflection은 런타임에 클래스, 메서드, 필드 등의 정보를 동적으로 조회하거나 조작할 수 있게 해주는 Java API입니다. JVM의 경우 클래스 로딩 시 Method 영역에 클래스에 대한 정보를 보관하는데, Reflection API는 이 정보를 사용하는 것 입니다.

생성 방법

JDK 동적 프록시의 경우 java.lang.reflect.Proxy의 newProxyInstance() 메서드를 이용해 프록시 객체를 생성합니다. 이에 대한 예시는 다음과 같습니다.

public interface TargetService {
    void method();
}

public class TargetServiceImpl implements TargetService {
    @Override
    public void method() {
        System.out.println("TargetServiceImpl 실행");
    }
}

public class ProxyExample {
    public static void main(String[] args) {
        
        TargetService proxy = (TargetService) Proxy.newProxyInstance(
        		// 프록시 클래스 로더
                TargetService.class.getClassLoader(),
                
                // 프록시가 구현할 인터페이스
                new Class[]{TargetService.class},                         
                
				// 메서드 호출 가로채는 로직
                new InvocationHandler() {
                
                    TargetService targetService = new TargetServiceImpl();

                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    
                        // 부가 기능
                        if (method.getName().equals("method")) {
                            System.out.println("[LOG] method 호출 전");
                        }

                        Object result = method.invoke(targetService, args);

                        if (method.getName().equals("method")) {
                            System.out.println("[LOG] method 호출 후");
                        }

                        return result;
                   
                    }
                    
                }
                
        );

        // 호출 시 프록시 객체가 대신 수행함
        //
        // [실행 결과]
        // [LOG] method 호출 전
        // TargetServiceImpl 실행
        // [LOG] method 호출 후
        proxy.method();
    }
}

CGLIB

CGLIB은 대상이 클래스로만 존재할 경우 사용하는 방법입니다. Spring의 경우 이 방법을 기본으로 사용하고 있습니다. 바이트코드 조작 방식으로 프록시를 만들기 때문에 동적 프록시에 비해 성능이 우수하다는 장점이 있는 반면, 상속 방식을 이용하기 때문에 final이나 private와 같은 경우 수행할 수 없습니다.

생성 방법

CGLIB의 경우 Enhancer 클래스를 이용해 프록시 객체를 생성합니다. 이에 대한 예시는 다음과 같습니다.


public class TargetService {
    public void method() {
        System.out.println("TargetService 실행");
    }

    public void anotherMethod() {
        System.out.println("다른 메서드 실행");
    }
}

public class CglibProxyExample {

    public static void main(String[] args) {
    
        // Enhancer를 통해 프록시 생성
        Enhancer enhancer = new Enhancer();
        
        // 부모 클래스로 지정
        enhancer.setSuperclass(TargetService.class); 
        
        // MethodInterceptor() 정의
        enhancer.setCallback(new MethodInterceptor() {
            
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            
                if (method.getName().equals("method")) {
                    System.out.println("[LOG] method 호출 전");
                }

				// 원본 메서드 호출
                Object result = proxy.invokeSuper(obj, args); 

                if (method.getName().equals("method")) {
                    System.out.println("[LOG] method 호출 후");
                }

                return result;
            }
            
        });

        // 프록시 객체 생성
        TargetService proxy = (TargetService) enhancer.create();
		
        // 프록시 적용 메서드
        // 
        // [실행 결과]
        // [LOG] method 호출 전
		// TargetService 실행
		// [LOG] method 호출 후
        proxy.method();         
        
        // 프록시 적용 X 메서드
        //
        // [실행 결과]
        // 다른 메서드 실행
        proxy.anotherMethod();  
    }
}
profile
동료들의 시간과 노력을 더욱 빛내줄 수 있는 개발자가 되고자 노력합니다.

0개의 댓글