Java for Beginner
674 subscribers
554 photos
156 videos
12 files
845 links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Dynamic Proxy в Java

Dynamic Proxy (динамические прокси) — это механизм в Java, позволяющий динамически создавать объекты, реализующие один или несколько интерфейсов, и управлять их поведением во время выполнения программы. Динамические прокси активно используются для написания кода с аспектно-ориентированным программированием (AOP), для реализации шаблонов Decorator и Proxy, а также для создания оберток над существующими классами.

Dynamic Proxy — это динамически генерируемые в рантайме классы, которые реализуют интерфейсы, указанные в параметрах. Динамические прокси позволяют перехватывать вызовы методов и управлять ими, а также добавлять или изменять функциональность классов, не меняя их исходного кода. В Java динамические прокси реализуются с помощью класса Proxy из пакета java.lang.reflect и интерфейса InvocationHandler.

Основные элементы динамических прокси:

Интерфейс InvocationHandler — определяет, как перехватываются и обрабатываются вызовы методов на проксируемом объекте.
Класс
Proxy — это встроенный класс в Java, который создает прокси-объекты для заданных интерфейсов.

Простая структура динамического прокси в Java выглядит следующим образом:
Создается интерфейс, который определяет методы.
Реализуется класс InvocationHandler, который перехватывает и обрабатывает вызовы этих методов.
С помощью метода
Proxy.newProxyInstance() создается прокси-объект, который будет перехватывать вызовы методов и передавать их InvocationHandler.

Внутреннее устройство Dynamic Proxy

Динамические прокси реализуются с помощью рефлексии и встроенного класса Proxy, который создает экземпляры прокси-объектов. Этот процесс можно разбить на несколько этапов:

Определение интерфейсов: Прокси-объект может реализовывать один или несколько интерфейсов. Это обязательное требование, так как динамические прокси могут проксировать только интерфейсы.
Реализация InvocationHandler: Это интерфейс, содержащий метод Object invoke(Object
proxy, Method method, Object[] args), который отвечает за перехват вызова метода на проксируемом объекте. Каждый раз, когда вызывается метод на прокси, invoke() обрабатывает этот вызов и может изменить поведение.
Создание прокси-объекта с помощью
Proxy.newProxyInstance(): Этот метод динамически создает новый класс, который реализует указанные интерфейсы и передает управление InvocationHandler.

Пример базовой реализации:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// Интерфейс
interface GreetingService {
void sayHello(String name);
}

// Реализация интерфейса
class GreetingServiceImpl implements GreetingService {
@Override
public void sayHello(String name) {
System.out.println("Hello, " + name);
}
}

// InvocationHandler для перехвата вызовов
class GreetingHandler implements InvocationHandler {
private Object target;

// Конструктор для передачи реального объекта
public GreetingHandler(Object target) {
this.target = target;
}

// Перехватчик вызова метода
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method: " + method.getName());
Object result = method.invoke(target, args); // Вызов реального метода
System.out.println("After method: " + method.getName());
return result;
}
}

public class DynamicProxyExample {
public static void main(String[] args) {
// Создание реального объекта
GreetingService realService = new GreetingServiceImpl();

// Создание прокси-объекта
GreetingService proxy = (GreetingService) Proxy.newProxyInstance(
realService.getClass().getClassLoader(),
realService.getClass().getInterfaces(),
new GreetingHandler(realService)
);

// Вызов метода на прокси-объекте
proxy.sayHello("John");
}
}


Вывод:
Before method: sayHello
Hello, John
After method: sayHello


#Java #Training #Medium #Dynamic_Proxy
Как работает Dynamic Proxy

Когда вызывается метод на прокси-объекте (например,
proxy.sayHello("John")), он перехватывается и передается в метод invoke() интерфейса InvocationHandler. Этот метод может модифицировать вызов, обрабатывать его и вызывать соответствующий метод на реальном объекте или полностью заменить его поведение.

Внутренние компоненты Dynamic Proxy:

Класс Proxy: Это специальный класс, который предоставляет статические методы для создания динамических прокси. Он генерирует байт-код нового класса в рантайме, который реализует все указанные интерфейсы.
Интерфейс InvocationHandler: Обрабатывает все вызовы методов, передавая их в invoke(). Это позволяет внедрять логику до и после вызова метода, измерять производительность, логировать и т. д.
Генерация байт-кода: Java использует библиотеку sun.misc.ProxyGenerator для динамического создания классов. Сгенерированный класс компилируется в байт-код и загружается с помощью ClassLoade
r.

Основные особенности и применение Dynamic Proxy

Изменение поведения объектов в рантайме: Dynamic Proxy используется для динамического изменения поведения объектов, когда нет доступа к исходному коду.
Простая реализация паттернов
Proxy и Decorator: Прокси позволяет легко оборачивать объекты в дополнительные слои логики, добавляя к ним новые функции или контролируя доступ.
Реализация аспектно-ориентированного программирования (AOP): В AOP используется для реализации таких аспектов, как логирование, управление транзакциями и безопасность.
Мокирование объектов в тестировании:
Dynamic Proxy упрощает создание тестовых объектов, которые имитируют поведение реальных классов для целей тестирования.

Примеры использования Dynamic Proxy

Логирование вызовов методов
С помощью InvocationHandler можно создать прокси, который будет логировать каждый вызов метода:
class LoggingHandler implements InvocationHandler {
private final Object target;

public LoggingHandler(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Invoking method: " + method.getName());
if (args != null) {
for (Object arg : args) {
System.out.println("Arg: " + arg);
}
}
return method.invoke(target, args);
}
}


Контроль доступа

С помощью динамического прокси можно управлять доступом к методам:
class SecurityHandler implements InvocationHandler {
private final Object target;

public SecurityHandler(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (!method.getName().equals("sayHello")) {
throw new IllegalAccessException("Access denied to method: " + method.getName());
}
return method.invoke(target, args);
}
}


Реализация кеширования результатов

Прокси можно использовать для реализации кэширования:
class CachingHandler implements InvocationHandler {
private final Object target;
private final Map<String, Object> cache = new HashMap<>();

public CachingHandler(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String key = method.getName() + Arrays.toString(args);
if (cache.containsKey(key)) {
return cache.get(key);
}
Object result = method.invoke(target, args);
cache.put(key, result);
return result;
}
}


#Java #Training #Medium #Dynamic_Proxy