target(최종적으로 요청을 처리하는 실제 object)에 대한 접근 방법을 제어하려는 목적
proxy pattern은 target의 기능 자체에는 관여하지 않고, 접근하는 방법을 제어해 주는 proxy를 이용하는 것
구조적으로 보면 decoreate(부가 기능)와 proxy(접근 제어 등)는 유사함, 다만 proxy는 코드에서 자신이 만들거나, 접근할 target class 정보를 알고 있는 경우가 많다.
public class UserServiceTx implements UserService { private TransactionManager transactionManager; private UserService userService; // target object public UserServiceTx(UserService userService) { this.userService = userService; } @Override public void add(User user) { userService.add(user); } @Override public void upgradeLevels() { TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition()); try { userService.upgradeLevles(); transactionManager.commit(status); } catch(RuntimeException e) { transactionManager.rollback(status); throw e; } } |
public Client { UserService userService = new UserServiceTx(); userService.add(new User()); userService.upgradeLevels(); } |
remote proxy
network 연결의 한 종단에 존재, network의 다른 쪽에 있는 객체와 동일한 interface를 구현
프록시 사용은 좋은 발상이지만, 사용하기 불편하다. 매번 새로운 class 정의하고, interface의 구현 메소드가 많으면 모든 method 구현햇 위임하는 코드 만들어야 함.
java.lang.reflect을 사용해 몇 가지 API를 이용해 proxy처럼 동작하는 object를 dynamic하게 생성하는 것
invoke method는 method와 parameter정보만 있으면 특정 object의 method를 실행할 수 있음
public class UserServiceTx implements InvocationHandler { private TransactionManager transactionManager; private Object target; public UserServiceTx(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.isAnnotationPresent(Transactional.class)) { return executeTx(method, args); } return method.invoke(target, args); } private Object executeTx(Method method, Object[] args) { TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition()); try { Object result = method.invoke(target, args); transactionManager.commit(status); return result; } catch(InvocationTargetException e) { transactionManager.rollback(status); throw e; } } } |
public Client { UserService target = new UserServiceImpl(); UserService userService = Proxy.newProxyInstance(target.getClass().getClassLoader(), new Class[]{UserService.class}, new UserServiceTx(target)); userService.add(new User()); userService.upgradeLevels(); } |