Всем привет!
Ну что ж до Нового года и старта десятого потока «Разработчик Java» осталось совсем шуть-шуть. Так что у нас остался один открытый урок, который мы подготавливаем для публикации и сегодняшняя заметка, из которой вы узнаете о динамическом прокси Java: что это такое, когда и как его использовать в коде.
Что такое прокси?
Прокси — это шаблон проектирования. Мы создаем и используем его для добавления и изменения функционала уже существующих классов. В таком случае, прокси-объект применяется вместо исходного. Обычно он использует тот же метод, что и оригинальный, и в Java прокси-классы расширяют исходные. Прокси может вызвать метод исходного объекта, так как у него есть дескриптор оригинала.
Таким образом, прокси-классы удобно реализуют многие вещи:
Все это происходит без изменений оригинального кода класса. Полный список не ограничивается примерами выше, они лишь его малая часть.
На практике, прокси-класс напрямую не реализует функционал. Следуя принципу единственной ответственности, прокси-класс непосредственно выполняет только проксирование, а изменение поведения реализуется в обработчиках. При вызове прокси-объекта вместо исходного, сам прокси решает, вызвать ли оригинальный метод или какие-то обработчики. Обработчик может выполнить как собственную задачу, так и обратиться к оригинальному методу.
Хоть шаблон прокси применяется не только для создания прокси-объекта и класса в среде выполнения, в Java это особенно интересная тема. В этой статье я фокусируюсь именно на таких прокси.
Это сложная тема, которая требует использования класса отражения, или манипулирования байт-кодом, или компиляции Java-кода, сгенерированного динамически. А может всего и сразу. Чтобы новый класс не был доступен в качестве байт-кода во время исполнения, потребуются сгенерированный байт-код и загрузчик классов для загрузки байт-кода. Для создания байт-кода, используйте cglib, bytebuddy или встроенный компилятор Java.
Важность разделения ответственностей, в нашем случае, становится ясна, стоит лишь подумать о прокси-классах и вызываемых ими обработчиках. Прокси-класс генерируется во время выполнения, но вызванные им обработчики могут быть добавлены в обычный исходный код и скомпилированы с остальной программой.
Как этим пользоваться в нашем коде?
Самое простое — использовать
Для вызова оригинального метода исходного объекта, обработчику необходим доступ к нему. Что не предоставлено реализацией прокси Java. Вам понадобится самостоятельно передать аргумент инстансу обработчика в коде. (Обратите внимание на объект (обычно с названием proxy), который передается в качестве аргумента вызываемому обработчику. Это прокси-объект, который отражение Java генерирует динамически, а не тот объект, что мы хотим проксировать.) Таким образом, вы можете использовать как отдельные объекты-обработчики для каждого исходного класса, так и общий объект, который знает, как вызвать оригинальный объект, если для этого вообще есть какой-либо метод.
В особом случае, вы можете создать обработчик вызова и прокси интерфейса без оригинального объекта. Более того, класс для реализации интерфейса в исходном коде — не требуется. Его реализует динамически созданный прокси-класс.
Если же проксируемый класс не реализует интерфейс, стоит задуматься об использовании какой-либо иной реализации прокси.
THE END
Ждём ваши комментарии и вопросы. Как всегда или тут, или можно зайти к Виталию на день открытых дверей.
Ну что ж до Нового года и старта десятого потока «Разработчик Java» осталось совсем шуть-шуть. Так что у нас остался один открытый урок, который мы подготавливаем для публикации и сегодняшняя заметка, из которой вы узнаете о динамическом прокси Java: что это такое, когда и как его использовать в коде.
Что такое прокси?
Прокси — это шаблон проектирования. Мы создаем и используем его для добавления и изменения функционала уже существующих классов. В таком случае, прокси-объект применяется вместо исходного. Обычно он использует тот же метод, что и оригинальный, и в Java прокси-классы расширяют исходные. Прокси может вызвать метод исходного объекта, так как у него есть дескриптор оригинала.
Таким образом, прокси-классы удобно реализуют многие вещи:
- логирование старта и остановки метода;
- дополнительную проверку аргументов;
- имитацию поведения исходного класса;
- реализацию отложенной инициализации затратных ресурсов;
Все это происходит без изменений оригинального кода класса. Полный список не ограничивается примерами выше, они лишь его малая часть.
На практике, прокси-класс напрямую не реализует функционал. Следуя принципу единственной ответственности, прокси-класс непосредственно выполняет только проксирование, а изменение поведения реализуется в обработчиках. При вызове прокси-объекта вместо исходного, сам прокси решает, вызвать ли оригинальный метод или какие-то обработчики. Обработчик может выполнить как собственную задачу, так и обратиться к оригинальному методу.
Хоть шаблон прокси применяется не только для создания прокси-объекта и класса в среде выполнения, в Java это особенно интересная тема. В этой статье я фокусируюсь именно на таких прокси.
Это сложная тема, которая требует использования класса отражения, или манипулирования байт-кодом, или компиляции Java-кода, сгенерированного динамически. А может всего и сразу. Чтобы новый класс не был доступен в качестве байт-кода во время исполнения, потребуются сгенерированный байт-код и загрузчик классов для загрузки байт-кода. Для создания байт-кода, используйте cglib, bytebuddy или встроенный компилятор Java.
Важность разделения ответственностей, в нашем случае, становится ясна, стоит лишь подумать о прокси-классах и вызываемых ими обработчиках. Прокси-класс генерируется во время выполнения, но вызванные им обработчики могут быть добавлены в обычный исходный код и скомпилированы с остальной программой.
Как этим пользоваться в нашем коде?
Самое простое — использовать
java.lang.reflect.Proxy
, который является частью JDK. Этот класс может создать прокси-класс или напрямую его инстанс. Пользоваться прокси, встроенным в Java, очень просто. Все что нужно — реализовать java.lang.InvocationHandler
, чтобы прокси-объект мог его вызывать. Интерфейс InvocationHandler
крайне прост и содержит только один метод: invoke()
. При его вызове, аргументы содержат проксируемый оригинальный объект, вызванный метод (как отражение объекта Method
) и массив объектов исходных аргументов. Фрагмент кода ниже демонстрирует применение:package proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkProxyDemo {
interface If {
void originalMethod(String s);
}
static class Original implements If {
public void originalMethod(String s) {
System.out.println(s);
}
}
static class Handler implements InvocationHandler {
private final If original;
public Handler(If original) {
this.original = original;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException {
System.out.println("BEFORE");
method.invoke(original, args);
System.out.println("AFTER");
return null;
}
}
public static void main(String[] args){
Original original = new Original();
Handler handler = new Handler(original);
If f = (If) Proxy.newProxyInstance(If.class.getClassLoader(),
new Class[] { If.class },
handler);
f.originalMethod("Hallo");
}
}
Для вызова оригинального метода исходного объекта, обработчику необходим доступ к нему. Что не предоставлено реализацией прокси Java. Вам понадобится самостоятельно передать аргумент инстансу обработчика в коде. (Обратите внимание на объект (обычно с названием proxy), который передается в качестве аргумента вызываемому обработчику. Это прокси-объект, который отражение Java генерирует динамически, а не тот объект, что мы хотим проксировать.) Таким образом, вы можете использовать как отдельные объекты-обработчики для каждого исходного класса, так и общий объект, который знает, как вызвать оригинальный объект, если для этого вообще есть какой-либо метод.
В особом случае, вы можете создать обработчик вызова и прокси интерфейса без оригинального объекта. Более того, класс для реализации интерфейса в исходном коде — не требуется. Его реализует динамически созданный прокси-класс.
Если же проксируемый класс не реализует интерфейс, стоит задуматься об использовании какой-либо иной реализации прокси.
THE END
Ждём ваши комментарии и вопросы. Как всегда или тут, или можно зайти к Виталию на день открытых дверей.
Источник: https://habr.com/company/otus/blog/434214/?utm_source=habrahabr&utm_medium=rss&utm_campaign=434214
Смотри также популярное:
Когда Java наконец помрёт, что с этим делать и что будет с JPoint. https://fetisovvs.blogspot.com/2018/11/java-jpoint-java.html
Разбор основных концепций параллелизма. http://fetisovvs.blogspot.com/2018/04/java.html
Первый контакт с «var» в Java 10. http://fetisovvs.blogspot.com/2018/01/var-java-10-java.html
JAVA 9. Что нового? http://fetisovvs.blogspot.com/2017/10/java-9-java.html
Руководство по Java 9 для тех, кому приходится работать с legacy-кодом. http://fetisovvs.blogspot.com/2018/08/java-9-legacy-java.html
Концепции объектно-ориентированного программирования — ООП в Java. http://fetisovvs.blogspot.com/2017/01/java-java.html
Анимации в Android по полочкам (Часть 1. Базовые анимации). http://fetisovvs.blogspot.com/2018/02/android-1-java.html
Двести пятьдесят русскоязычных обучающих видео докладов и лекций о Java. http://fetisovvs.blogspot.com/2015/12/java-5-java-java.html
Абстрактные классы и методы. http://fetisovvs.blogspot.com/2017/02/java.html
Полное руководство по Java Reflection API. Рефлексия на примерах. http://fetisovvs.blogspot.com/2017/02/java-reflection-api-java.html
Микросервисы для Java программистов. Практическое введение во фреймворки и контейнеры. http://fetisovvs.blogspot.com/2017/10/java-java.html
Микросервисы для Java программистов. Практическое введение во фреймворки и контейнеры. (Часть 3). http://fetisovvs.blogspot.com/2017/10/java-3-java.html
ТОП-3 способа конвертировать массив в ArrayList. Пример на Java. http://fetisovvs.blogspot.com/2016/09/3-arraylist-java-java.html
Ввод–вывод в Java. http://fetisovvs.blogspot.com/2016/05/java-java_28.html
Java Challengers #2: Сравнение строк. https://fetisovvs.blogspot.com/2018/11/java-challengers-2-java.html
Enum-Всемогущий. http://fetisovvs.blogspot.com/2017/02/enum-java.html
Массивы в Java. Создание и обработка. http://fetisovvs.blogspot.com/2017/10/java-java_18.html
Arrays, Collections: Алгоритмический минимум. http://fetisovvs.blogspot.com/2017/12/arrays-collections.html
Популярные методы для работы с Java массивами. http://fetisovvs.blogspot.com/2016/09/java-java_29.html
Пример использования метода replace в Java. Как заменить символ в строке? http://fetisovvs.blogspot.com/2017/01/replace-java-java.html
Класс Scanner в Java — описание и пример использования. http://fetisovvs.blogspot.com/2017/01/scanner-java-java.html
Пример использования метода trim в Java: как удалить пробелы в начале и конце строки? http://fetisovvs.blogspot.com/2017/01/trim-java-java.html
Spark — Потрясающий веб-микрофреймворк для Java. http://fetisovvs.blogspot.com/2017/10/spark-java-java.html
Чтение и запись CSV файла с помощью SuperCSV. http://fetisovvs.blogspot.com/2017/01/csv-supercsv-java-java.html
Конструкция try/catch/finally (исключения). http://fetisovvs.blogspot.com/2017/01/trycatchfinally-java.html
1000+ часов видео по Java на русском. http://fetisovvs.blogspot.nl/2017/06/1000-java-java.html
Раздача халявы: нетормозящие треды в Java. Project Loom. http://fetisovvs.blogspot.com/2018/09/java-project-loom-java.html
Шпаргалка Java программиста 7.1 Типовые задачи: Оптимальный путь преобразования InputStream в строку. http://fetisovvs.blogspot.com/2016/04/java-71-inputstream-java.html
Шпаргалки Java программиста 10: Lombok. http://fetisovvs.blogspot.nl/2017/12/java-10-lombok-java.html
Шпаргалки Java программиста 9: Java SE — Шпаргалка для собеседований и повторений. http://fetisovvs.blogspot.com/2017/12/java-9-java-se-java.html
Шпаргалка Java программиста 8. Библиотеки для работы с Json (Gson, Fastjson,
LoganSquare, Jackson, JsonPath и другие). http://fetisovvs.blogspot.com/2016/04/java-8-json-gson-fastjson-logansquare.html
Java 8 и паттерн Стратегия. http://fetisovvs.blogspot.com/2018/03/java-8-java.html
Java EE Concurency API. http://fetisovvs.blogspot.com/2018/08/java-ee-concurency-api-java.html
Реализация ООП-наследования в классах, работающих с SQL и MS Entity Framework. http://fetisovvs.blogspot.com/2017/02/sql-ms-entity-framework.html
Как установить соединение с СУБД MySQL в IntelliJ IDEA в редакции Community. http://fetisovvs.blogspot.com/2016/04/mysql-intellij-idea-community-java.html
TDD приложений на Spring Boot: работа с базой данных. https://fetisovvs.blogspot.com/2018/12/tdd-spring-boot-java.html
Максимально простой в поддержке способ интеграции java-клиента с java-сервером. http://fetisovvs.blogspot.com/2018/09/java-java-java.html
Как с помощью maven работать с библиотеками, которых в maven нет. http://fetisovvs.blogspot.com/2017/03/maven-maven-java.html
Проекты по созданию компиляторов из Java в JavaScript и исполняемые файлы. http://fetisovvs.blogspot.com/2018/01/java-javascript-java.html
Реактивное программирование с JAX-RS. http://fetisovvs.blogspot.com/2018/09/jax-rs-java.html
Компактные строки в Java 9. https://fetisovvs.blogspot.com/2018/10/java-9-java.html
Абстрактный CRUD от репозитория до контроллера: что ещё можно сделать при помощи Spring + Generics. http://fetisovvs.blogspot.com/2018/09/crud-spring-generics-java.html
Диагностика утечек памяти в Java. http://fetisovvs.blogspot.com/2017/03/java-java_18.html
Spring AOP и JavaConfig в плагинах для Atlassian Jira. http://fetisovvs.blogspot.com/2018/04/spring-aop-javaconfig-atlassian-jira.html
Блеск и нищета Java для настольных систем. http://fetisovvs.blogspot.com/2018/04/java-haulmont-java.html
Разбор задачек от Одноклассников на JPoint 2018. http://fetisovvs.blogspot.com/2018/04/jpoint-2018-java.html
Программируем… выход из лабиринта. http://fetisovvs.blogspot.com/2015/10/java.html
Основы работы с IntelliJ IDEA. Интерфейс программы. http://fetisovvs.blogspot.com/2016/09/intellij-idea-java.html
Ускоряем время сборки и доставки java web приложения. http://fetisovvs.blogspot.com/2018/03/java-web-java.html
Открытый урок Java Enterprise «CDI in action». http://fetisovvs.blogspot.com/2018/09/java-enterprise-cdi-in-action-java.html
«Мы все стремимся к сложности, а потом с ней боремся»: интервью с Венкатом Субраманиамом. http://fetisovvs.blogspot.com/2018/09/java_16.html
Комментариев нет:
Отправить комментарий