문제의 역사:
Dynamic Proxy Pattern은 Java 1.3에서 등장하여 AOP 패턴, 로깅, 보안 및 수동으로 프록시 클래스를 작성하지 않고 코드의 동적 생성을 가능하게 하였습니다. 이는 Spring, Hibernate 및 기타 프레임워크에서 매우 인기를 얻었습니다.
문제점:
때때로 기존 인터페이스에 기능을 추가하거나 실행 중에 메소드의 동작을 변경해야 할 필요가 있으며, 이는 исходный код을 변경하지 않고도 가능합니다. 예시: 각 메소드 호출의 로깅, 접근 검사, 프로파일링. 주요 문제는 모든 클래스에 대해 이를 "수동으로" 수행할 수 없다는 것입니다, 특히 클래스가 많거나 동적으로 생성되는 경우.
해결 방법:
Java는 java.lang.reflect 패키지에서 Proxy.newProxyInstance를 사용하여 인터페이스를 위한 "온디맨드" 프록시 객체를 생성할 수 있는 표준 API를 제공합니다. 각 메소드 호출을 수신하고 자신의 로직을 구현하는 InvocationHandler 객체를 구현합니다.
코드 예시:
import java.lang.reflect.*; interface Service { void serve(); } class ServiceImpl implements Service { public void serve() { System.out.println("Serving..."); } } class LoggingHandler implements InvocationHandler { private final Object target; public LoggingHandler(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Calling " + method.getName()); return method.invoke(target, args); } } Service service = (Service) Proxy.newProxyInstance( Service.class.getClassLoader(), new Class[]{Service.class}, new LoggingHandler(new ServiceImpl())); service.serve();
주요 특징:
표준 dynamic proxy를 사용하여 인터페이스를 구현하지 않는 일반 클래스를 "랩"할 수 있는가?
아니요. Dynamic Proxy는 인터페이스에서만 작동합니다. 클래스의 경우 바이트코드 조작이 필요합니다 (예: CGLIB).
인터페이스에 반환 타입이 InvocationHandler의 반환값과 호환되지 않는 메소드가 포함되어 있으면 어떻게 되는가?
ClassCastException이 발생하므로 반환 값은 인터페이스의 정의와 엄격하게 호환되어야 합니다.
final 메소드에 대해 dynamic proxy를 사용할 수 있는가?
아니요. Proxy는 인터페이스 수준에서만 작동하며 final 메소드는 존재하지 않습니다. Final 메소드는 전혀 오버라이드할 수 없습니다.
부정적인 사례
개발자가 인터페이스가 없는 클래스에 Proxy를 통해 감사 로깅을 구현하려고 시도하였습니다.
장점:
단점:
긍정적인 사례
계약을 인터페이스로 분리하고 그에 따라 프록시 포장을 만들었습니다.
장점:
단점: