Dynamic Proxy

Dynamic Proxy

Proxy

프록시는 대리자, 대리인 즉, 무언가를 대신해준다는 뜻을 가지고 있습니다.

프록시 패턴도 무언가를 대신해주는 객체를 이용하는 패턴이라고 볼 수 있습니다.

간단하게 예제 먼저 살펴보겠습니다.

interface IService { String service(int money); } class RealService implements IService { @Override public String service(int money) { if (money < 1000) { return "실패했습니다."; } else { return "구매했습니다.."; } } } class ProxyService implements IService { IService iService; public ProxyService(IService iService) { this.iService = iService; } @Override public String service(int money) { String s; if (money < 1000) { System.out.println("금액이 부족합니다!"); int debt = 1000- money; System.out.println(debt + "$을 빌렸습니다!"); return iService.service(1000); } else { return iService.service(1000); } } } public class Main { public static void main(String[] args) { IService proxy = new ProxyService(new RealService()); System.out.println(proxy.service(100)); } } /* 금액이 부족합니다! 900$을 빌렸습니다! 구매했습니다.. */

프록시 패턴은 실제 서비스 객체가 가진 메서드와 같은 이름의 메서드를 사용하는데 이를 위해 인터페이스를 사용합니다. 메서드를 호출하고 반환 값을 받는지, 프록시 객체를 통해 메서드를 호출하고 반환 값을 받는지 전혀 모르게 처리할 수 있습니다.

프록시는 실제 서비스와 같은 이름의 메서드를 구현합니다. (인터페이스)

프록시는 실제 서비스에 대한 참조 변수를 갖습니다 (합성)

프록시는 실제 서비스의 같은 이름을 가진 메소드를 호출하고 그 값을 클라이언트에게 돌려줍니다.

프록시는 실제 서비스 메서드 호출 전후로 별도의 로직을 수행할 수 있습니다.

정리하자면 프록시 패턴은 제어의 흐름을 조정하기 위한 목적으로 대리자를 사용하는 패턴이라고 이야기할 수 있습니다.

Dynamic Proxy

하지만 위 예제처럼 프록시 객체를 직접 생성해서 사용하는 것은 사실 귀찮은 작업이기도 하고, 관리해줘야 할 클래스가 하나 더 늘어남을 의미합니다. 만약 메소드의 종류가 많다면 그 많은 메소드들을 다 오버라이딩해줘야합니다. 이러한 단점은 Dynamic Proxy를 이용해서 어느정도 보완할 수 있습니다. Dynamic Proxy는 런타임 시점에 인터페이스를 구현하는 클래스 또는 인터페이스를 만드는 기술입니다.

Dynamic Proxy를 위해 java.lang.reflect.Proxy 클래스를 생성해야하는데 Proxy 객체는 new 연산자로 생성할 수 없고, newPorxyInstance를 통해 생성할 수 있습니다.

public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) throws IllegalArgumentException

ClassLoader loader: 프록시 클래스를 가상머신으로 로드할 클래스 로더

Class [] interfaces: 프록시가 구현해야할 인터페이스의 Class 객체로 구성된 배열

InvocationHandler h: 호출 핸들러

InvocationHandler는 invoke() 메서드를 가지고있는 함수형 인터페이스입니다.

public Object invoke(Object proxy, Method method, Object[] args)

Object proxy, // 호출 핸들러를 호출한 프록시 객체

Method method, // 프록시를 통해 호출한 Method 객체

Object[] args // 프록시를 통해 호출한 메소드의 인자 배열

코드 예시

public class Main { public static void main(String[] args) { IService service = (IService) Proxy.newProxyInstance( RealService.class.getClassLoader(), new Class[] {IService.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { int money = (int) args[0]; String invoke = ""; if (money < 1000) { System.out.println("금액이 부족합니다!"); int debt = 1000- money; args[0] = 1000; System.out.println(debt + "$을 빌렸습니다!"); invoke = (String) method.invoke(new RealService(), args); } else { invoke = (String) method.invoke(new RealService(), args); } System.out.println("다음에 또 오세요!"); return invoke; } } ); System.out.println(service.service(100)); } }

from http://jinukix.tistory.com/51 by ccl(A) rewrite - 2021-10-18 13:27:21