← Назад к вопросам

Что такое Java Dynamic Proxy?

2.2 Middle🔥 171 комментариев
#JVM и память#Архитектура и паттерны

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое Java Dynamic Proxy?

Java Dynamic Proxy (динамический прокси) — это механизм языка Java, позволяющий создавать объекты-посредники (proxy objects) во время выполнения программы (runtime). Эти объекты "оборачивают" целевой объект и перехватывают вызовы его методов, позволяя добавлять дополнительную логику (например, логирование, проверки прав доступа, кеширование) без изменения исходного кода целевого класса.

Ключевые принципы работы

Динамический прокси реализуется с помощью двух основных компонентов:

  1. java.lang.reflect.Proxy — фабричный класс для создания прокси-объектов.
  2. java.lang.reflect.InvocationHandler — интерфейс, который ты реализуешь для описания логики перехвата вызовов.

Как это работает?

  1. Прокси-объект создаётся для одного или нескольких интерфейсов (но не для классов!).
  2. Все вызовы методов этого прокси перенаправляются в единственный метод invoke() реализованного тобой InvocationHandler.
  3. Внутри invoke() ты получаешь полную информацию о вызове: метод, аргументы, сам прокси-объект. Ты можешь выполнить код до и после вызова целевого метода, или вообще не вызывать его.

Пример кода

Представим, что у нас есть простой интерфейс сервиса:

// Интерфейс, который будем проксировать
public interface UserService {
    String getUserName(int id);
    void deleteUser(int id);
}

И его реализация:

public class UserServiceImpl implements UserService {
    @Override
    public String getUserName(int id) {
        return "User_" + id;
    }

    @Override
    public void deleteUser(int id) {
        System.out.println("Удалён пользователь с id: " + id);
    }
}

Теперь создадим InvocationHandler для добавления логирования:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public 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("Вызван метод: " + method.getName());
        if (args != null) {
            for (Object arg : args) {
                System.out.println("Аргумент: " + arg);
            }
        }

        // Сам вызов целевого метода
        Object result = method.invoke(target, args);

        // Логирование после вызова
        System.out.println("Метод " + method.getName() + " завершён");
        return result;
    }
}

И, наконец, создание и использование прокси:

import java.lang.reflect.Proxy;

public class Main {
    public static void main(String[] args) {
        UserService realService = new UserServiceImpl();
        
        // Создаём динамический прокси
        UserService proxyService = (UserService) Proxy.newProxyInstance(
                UserService.class.getClassLoader(), // ClassLoader
                new Class[]{UserService.class},     // Массив интерфейсов
                new LoggingHandler(realService)     // Наш обработчик
        );

        // Вызовы идут через прокси
        proxyService.getUserName(42);
        proxyService.deleteUser(100);
    }
}

Вывод в консоли:

Вызван метод: getUserName
Аргумент: 42
Метод getUserName завершён
Вызван метод: deleteUser
Аргумент: 100
Удалён пользователь с id: 100
Метод deleteUser завершён

Ключевые особенности и ограничения

  • Только для интерфейсов: Динамический прокси может реализовывать только интерфейсы, но не классы. Для проксирования классов используется CGLIB или Byte Buddy.
  • Производительность: Вызовы через динамический прокси медленнее прямых вызовов из-за рефлексии, но для большинства задач (логирование, транзакции) это некритично.
  • Гибкость: Один обработчик (InvocationHandler) может использоваться для разных интерфейсов и методов.

Применение в Android-разработке

Хотя в Android чаще используются другие подходы (например, Dagger для внедрения зависимостей), динамические прокси находят применение в:

  • Retrofit — библиотека для сетевых запросов создаёт реализации интерфейсов API с помощью динамических прокси.
  • Системы событий (Event Bus) — для перехвата и маршрутизации событий.
  • Декораторы и интерцепторы — добавление сквозной функциональности (например, аутентификации) к группам методов.

Сравнение с другими подходами

  • Статическое прокси (ручное): Требует создания отдельного класса для каждого проксируемого интерфейса. Динамическое прокси устраняет эту необходимость.
  • Аспектно-ориентированное программирование (AOP): Dynamic Proxy — это одна из простейших реализаций AOP в Java, позволяющая реализовать сквозную функциональность (cross-cutting concerns).

Таким образом, Java Dynamic Proxy — это мощный инструмент метапрограммирования, который позволяет отделить вспомогательную логику от бизнес-кода, следуя принципу единственной ответственности (Single Responsibility Principle). Его понимание важно для работы с современными Java-библиотеками и фреймворками.

Что такое Java Dynamic Proxy? | PrepBro