Статьи

Создание динамического прокси Java

Механизм Java Dynamic proxy предоставляет интересный способ создания экземпляров прокси. Шаги по созданию динамического прокси немного утомительны, но рассмотрим прокси, который будет использоваться для проверки времени, затрачиваемого на вызов метода для экземпляра службы —

1
2
3
4
5
6
7
8
public interface InventoryService {
    public Inventory create(Inventory inventory);
    public List<Inventory> list();
    public Inventory findByVin(String vin);
    public Inventory update(Inventory inventory);
    public boolean delete(Long id);
    public Inventory compositeUpdateService(String vin, String newMake);
}

Шаги для создания динамического прокси-сервера для экземпляров этого интерфейса следующие:

1. Создайте экземпляр java.lang.reflect.InvocationHandler , он будет отвечать за обработку вызовов методов от имени фактического экземпляра службы, пример обработчика вызова для аудита:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
...
public class AuditProxy implements java.lang.reflect.InvocationHandler {
 
    private Object obj;
 
    public static Object newInstance(Object obj) {
        return java.lang.reflect.Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
                .getClass().getInterfaces(), new AuditProxy(obj));
    }
 
    private AuditProxy(Object obj) {
        this.obj = obj;
    }
 
    public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
        Object result;
        try {
            logger.info("before method " + m.getName());
            long start = System.nanoTime();
            result = m.invoke(obj, args);
            long end = System.nanoTime();
            logger.info(String.format("%s took %d ns", m.getName(), (end-start)) );
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        } catch (Exception e) {
            throw new RuntimeException("unexpected invocation exception: " + e.getMessage());
        } finally {
            logger.info("after method " + m.getName());
        }
        return result;
    }
}

2. При создании экземпляров InventoryService возвращайте прокси-сервер, который в данном случае является AuditProxy, составляя экземпляры InventoryService, что можно лучше объяснить с помощью UML:

Вот как это будет выглядеть в коде:

1
InventoryService inventoryService = (InventoryService)AuditProxy.newInstance(new DefaultInventoryService());

Теперь любые вызовы inventoryService будут выполняться через экземпляр AuditProxy, который будет измерять время, затраченное на метод, при делегировании фактического вызова метода экземпляру InventoryService.

Итак, для чего используются прокси:
1. Spring AOP использует его широко — он внутренне создает динамический прокси для различных конструкций AOP
2. Как в этом примере, для любого украшения класса — АОП определенно будет лучше подходить для такого случая использования, хотя
3. Для любых сред, нуждающихся в поддержке функций на основе интерфейса и аннотаций. Реальный прокси-экземпляр даже не должен существовать, динамический прокси-сервер может воссоздать ожидаемое поведение интерфейса на основе некоторых метаданных, предоставляемых аннотациями.

Ссылка: Создание динамического прокси Java от нашего партнера по JCG Биджу Кунджуммена в блоге all and sundry.