Полезность динамических прокси-серверов Java и обычных прокси-серверов
Мне нужен совет, какие сценарии динамический прокси оказался бы более полезным, чем обычный прокси.
Я приложил много усилий, чтобы научиться эффективно использовать динамические прокси. В этом вопросе следует отметить, что такие структуры, как AspectJ, могут выполнять в основном все, что мы пытаемся достичь с помощью динамических прокси, или, например, CGLIB можно использовать для устранения некоторых недостатков динамических прокси.
Использовать случаи
- Декораторы - например, выполнять ведение журнала при вызове метода или возвращаемые кешем значения сложных операций
- Подтверждение контракта. То есть, убедитесь, что параметры находятся в допустимом диапазоне, а типы возврата соответствуют принятым значениям.
- Адаптер - увидел какую-нибудь умную статью где-то, описывающую, как это полезно. Тем не менее, я редко встречаю этот шаблон дизайна.
Другие?
Преимущества динамического прокси-сервера
- Decorator: регистрировать все вызовы методов, например,
public Object invoke(Object target, Method method, Object[] arguments) {
System.out.println("before method " + method.getName());
return method.invoke(obj, args);
}
}
Шаблон декоратора определенно полезен, так как он позволяет использовать побочные эффекты для всех методов прокси (хотя это поведение является примером для использования аспектов...).
- Контракт: в отличие от обычного прокси, нам не нужно реализовывать полный интерфейс. Например.,
public Object invoke(Object target, Method method, Object[] arguments) {
if ("getValues".equals(method.getName()) {
// check or transform parameters and/or return types, e.g.,
return RangeUtils.validateResponse( method.invoke(obj, args) );
}
if ("getVersion".equals(method.getName()) {
// another example with no delegation
return 3;
}
}
Контракт, с другой стороны, дает только возможность избежать полного внедрения интерфейса. Опять же, рефакторинг прокси-методов будет бессильно отменять динамический прокси.
Заключение
Итак, я вижу здесь один реальный случай использования и один сомнительный прецедент. Что вы думаете?
Ответы
Ответ 1
Существует множество потенциальных применений для динамических прокси-серверов, выше описанных вами -
- Публикация событий - по методу x(), прозрачно вызовите y() или отправьте сообщение z.
- Управление транзакциями (для соединений db или других транзакционных операций)
- Управление потоками - прозрачная обработка дорогостоящих операций.
- Отслеживание производительности - операции синхронизации, проверенные, например, CountdownLatch.
- Управление подключением - мышление API, например Salesforce Enterprise API, требующее от клиентов своей службы запуска сеанса перед выполнением любых операций.
- Изменение параметров метода - если вы хотите передать значения по умолчанию для нулей, если это что-то вроде этого.
Это всего лишь несколько опций в дополнение к проверке и регистрации, как описано выше. FWIW, JSR 303, спецификация валидации bean, имеет реализацию AOP-стиля в Hibernate Validator, поэтому вам не нужно ее реализовывать специально для ваших объектов данных. Структура Spring также имеет встроенную валидацию и имеет действительно приятную интеграцию с AspectJ для некоторых описанных здесь вещей.
Ответ 2
Действительно, АОП использует большинство динамических прокси. Это потому, что вы можете создать динамический прокси-сервер вокруг объекта, который вы не знаете заранее.
Еще одна полезная сторона динамического прокси-сервера - это когда вы хотите применить одну и ту же операцию ко всем методам. С помощью статического прокси-сервера вам понадобится много дублированного кода (по каждому прокси-методу вам понадобится один и тот же вызов метода, а затем делегировать его прокси-объекту), а динамический прокси минимизирует это.
Также обратите внимание, что Adapter and Decorator - это отдельные шаблоны. Они выглядят как шаблон прокси в том виде, в котором они реализованы (по композиции объекта), но они служат другой цели:
- шаблон декоратора позволяет вам иметь несколько бетонных декораторов, тем самым добавляя функциональность во время выполнения
- шаблон адаптера предназначен для адаптации объекта к интерфейсу unmatching. Лучший пример, который я могу придумать, - это
EnumetationIterator
- он адаптирует интерфейс Enumeration
к интерфейсу Iterator
.
Ответ 3
Другим вариантом использования, о котором я могу думать, является динамическое внедрение интерфейсов во время выполнения, что и работает с некоторыми фреймворками.
Возьмите, например, Retrofit, библиотеку Java для использования служб REST. Вы определяете интерфейс Java, который отражает операции, доступные в REST API, и украшайте методы аннотациями для настройки специфики запроса. Легко видеть, что в этом случае все методы, определенные в интерфейсе, должны выполнять HTTP-запрос на каком-либо сервере, преобразовывать аргументы метода в параметры запроса; и затем проанализировать ответ в объект java, определенный как возвращаемый тип метода.