История вопроса:
Dynamic Proxy Pattern появился в Java 1.3 и позволил реализовывать паттерны AOP, логгирование, безопасность, динамическую генерацию кода без ручного написания прокси-классов. Это стало очень популярным в Spring, Hibernate и других фреймворках.
Проблема:
Иногда требуется добавить функционал к уже существующему интерфейсу или изменять поведение методов во время выполнения, не меняя исходный код. Пример: логгирование каждого вызова метода, проверка доступа, профилирование. Основная проблема — невозможность сделать это "ручками" для всех классов, особенно если классов много или они генерируются динамически.
Решение:
Java предоставляет стандартный API в пакете java.lang.reflect для создания прокси-объектов "на лету" для любых интерфейсов с помощью Proxy.newProxyInstance. Реализуется объект 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 работает только с интерфейсами. Для классов нужен bytecode manipulation (например, CGLIB).
Что произойдет, если интерфейс содержит методы с возвращаемым типом, несовместимым со значением возврата из InvocationHandler?
Будет выброшено ClassCastException, поэтому возвращаемое значение должно быть строго совместимо с определением интерфейса.
Можно ли использовать dynamic proxy для final методов?
Нет. Proxy работает только на уровне интерфейсов, а там final методов не бывает. Final методы нельзя переопределить вообще.
Негативный кейс
Разработчик попытался внедрить audit-логгирование через Proxy на классы без интерфейса
Плюсы:
Минусы:
Позитивный кейс
Выделили контракт интерфейсом, уже под него сделали proxy-обертку
Плюсы:
Минусы: