JDK 동적 프록시
JDK 동적 프록시는 Java에서 리플렉션을 활용하여 인터페이스를 구현한 클래스의 프록시를 런타임에 동적으로 생성하는 기술이다.
이를 통해 메서드 호출 등의 작업을 프록시 객체를 통해 중간에서 처리할 수 있다.
JDK 동적 프록시의 한계점
- 인터페이스만 프록시 가능 : JDK 동적 프록시는 인터페이스를 구현한 클래스에만 적용할 수 있으며, 클래스의 메서드에는 적용할 수 없다.
- final 메서드와 private 메서드 : final로 선언된 메서드나 private 메서드는 프록시로 래핑할 수 없다.
- 생성자 호출 : JDK 동적 프록시는 생성자 호출을 가로채지 못하므로, 객체 생성 시 특별한 작업을 수행할 수 없다.
JDK 동적 프록시의 구현 원리와 기술
JDK 동적 프록시는 ‘java.lang.reflect.Proxy’클래스와 InvocationHandler 인터페이스를 활용한다.
Proxy.newProxyInstance() 메서드를 사용해 프록시 객체를 생성하고, InvocationHanlder를 구현한 클래스를 통해 메서드 호출을 가로채고 처리한다.
동작 과정
- 사용자는 프록시를 생성하기 위해 Proxy.newProxyInstance() 메서드를 호출하고, 필요한 인터페이스와 InvocationHandler를 전달한다.
- JDK는 전달받은 인터페이스를 구현한 동적 프록시 클래스를 생성한다.
- 프록시 클래스의 메서드를 호출하면 InvocationHandler의 invoke() 메서드가 호출된다.
- invoke() 메서드에서 사용자가 정의한 로직을 수행한 후 실제 대상 객체의 메서드를 호출한다.
스프링 예제
//1. 인터페이스 정의
public interface Service {
void method();
}
//2. 인터페이스를 구현한 실제 서비스 클래스 정의
public class ServiceImpl implements Service {
@Override
public void method(){
//실제 동작 정의
}
}
//3. JDK 동적 프록시 생성 및 빈으로 등록
@Configuration
public class ProxyConfig{
@Bean
public Service service(){
ProxyFactryBean proxyFactoryBean = new ProxyFactoryBean();
//프록시 대상 클래스 설정
proxyFactoryBean.setTarget(new ServiceImpl());
//setInterfaces(Service.class) 로 인터페이스 직접 지정 가능하나, 알아서 찾아준다.
proxy.addAdvice(new CustomAdvice()); //부가기능 추가 여러개도 가능
return (Service) proxyFactoryBean.getObject(); //프록시 반환
}
//4. 부가기능 구현
class CustomAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// 타깃 오브젝트를 전달할 필요 없음. 메소드 정보와 타깃 오브젝트를 이미 알고 있음
String ret = (String)invocation.proceed();
// 부가 기능 적용
return ret.toUpperCase();
}
}
CGLIB란?
CGLIB(Code Generation Library)는 JDK 동적 프록시의 한계를 극복하기 위한 라이브러리로, 클래스 단위의 프록시를 생성하는데 사용된다. 인터페이스 없이도 클래스의 메서드에 프록시를 적용할 수 있으며, 생성자 호출과 final 메서드도 프록시로 처리할 수 있다.
CGLIB vs JDK 동적 프록시
CGLIB는 JDK 동적시의 한계점인 인터페이스 제약을 없애고, 클래스의 메서드에도 프록시를 적용할 수 있다.
또한 final 메서드와 private 메서드도 프록시로 처리할 수 있다.
몇 버전 부터 CGLIB가 디폴트이고, 왜?
Spring 2.0 부터 CGLIB를 기본 프록시 생성 라이브러리로 사용하게 되었다.
스프링은 프록시를 사용해서 AOP 기능을 구현하는데, JDK 동적 프록시는 인터페이스를 기반으로 동작하므로, 인터페이스를 갖추지 않는 클래스에 대해서는 JDK 동적 프록시를 생성할 수 없다.
이로 인해 CGLIB가 필요하게 되었고, 스프링은 JDK 동적프록시보다 더 다양한 경우에 대응할 수 있는 CGLIB를 기본으로 선택하게 되었다.
CGLIB 동작 과정
- 사용자가 프록시 대상 클래스를 정의
- 스프링 컨테이너가 프록시 대상 클래스를 감싸는 CGLIB 프록시 클래스를 생성
- 클라이언트가 프록시 객체의 메서드를 호출하면 CGLIB 프록시 객체의 메서드가 실행된다.
- CGLIB 프록시 객체의 메서드는 필요한 경우 대상 객체의 메서드를 호출하고, 추가적인 로직을 수행할 수 있다.
CGLIB 구현원리와 기술
CGLIB는 자바 바이트 코드를 직접 생성하고, 수정하여 프록시 객체를 만든다.
즉, 바이트 코드 조작 기술을 사용해 프록시 대상 클래스의 하위 클래스를 생성하고, 대상 클래스의 메서드를 오버라이드하여 프록시 동작을 추가한다.
'Back-end' 카테고리의 다른 글
불변성 (0) | 2023.09.08 |
---|---|
Spring DI 방식 (0) | 2023.09.08 |
상속보다 합성을 이용하는 이유 (0) | 2023.09.06 |
스레드를 무한정 만든다면? (0) | 2023.08.31 |
PSA(Portable Service Abstraction) (0) | 2023.08.30 |