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

От чего зависит Dynamic Proxy?

1.7 Middle🔥 61 комментариев
#Безопасность

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

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

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

От чего зависит Dynamic Proxy в Java

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

Основные компоненты Dynamic Proxy

Динамический прокси зависит от трёх ключевых элементов:

1. Интерфейс

  • Dynamic Proxy работает только с интерфейсами (в стандартном java.lang.reflect.Proxy)
  • Proxy должен реализовывать существующий интерфейс
  • Проксируемый объект должен реализовывать этот интерфейс

2. ClassLoader

  • Proxy класс создается определённым ClassLoader'ом
  • ClassLoader должен иметь доступ к интерфейсу
  • От выбора ClassLoader зависит, в каком пакете будет proxy класс

3. InvocationHandler

  • Это главный игрок — обработчик всех вызовов методов
  • InvocationHandler перехватывает методы и выполняет логику

Как это работает в коде

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

// Реальная реализация
public class UserServiceImpl implements UserService {
    @Override
    public void saveUser(String name) {
        System.out.println("Saving user: " + name);
    }
    
    @Override
    public User getUser(int id) {
        System.out.println("Getting user: " + id);
        return new User(id, "John");
    }
    
    @Override
    public void deleteUser(int id) {
        System.out.println("Deleting user: " + id);
    }
}

Создание InvocationHandler

Это сердце Dynamic Proxy. InvocationHandler перехватывает ВСЕ вызовы методов:

public class LoggingInvocationHandler implements InvocationHandler {
    private Object target;  // Реальный объект
    
    public LoggingInvocationHandler(Object target) {
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) 
            throws Throwable {
        
        // ДО вызова метода
        System.out.println("[LOG] Calling method: " + method.getName());
        System.out.println("[LOG] Arguments: " + Arrays.toString(args));
        long startTime = System.currentTimeMillis();
        
        try {
            // Вызываем реальный метод
            Object result = method.invoke(target, args);
            
            // ПОСЛЕ успешного вызова
            long duration = System.currentTimeMillis() - startTime;
            System.out.println("[LOG] Method completed in: " + duration + "ms");
            
            return result;
        } catch (Throwable e) {
            // При ошибке
            System.out.println("[ERROR] Exception in: " + method.getName());
            throw e;
        }
    }
}

Создание Dynamic Proxy

public class ProxyFactory {
    public static UserService createUserServiceProxy(UserService target) {
        // Параметры для создания proxy:
        // 1. ClassLoader
        // 2. Интерфейсы для реализации
        // 3. InvocationHandler
        
        return (UserService) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),           // ClassLoader
            target.getClass().getInterfaces(),            // Интерфейсы
            new LoggingInvocationHandler(target)          // Handler
        );
    }
}

Использование

public class Main {
    public static void main(String[] args) {
        // Создаём реальный объект
        UserService realService = new UserServiceImpl();
        
        // Оборачиваем его в proxy
        UserService proxyService = ProxyFactory
            .createUserServiceProxy(realService);
        
        // Используем proxy — все вызовы логируются
        proxyService.saveUser("Alice");
        // Output:
        // [LOG] Calling method: saveUser
        // [LOG] Arguments: [Alice]
        // Saving user: Alice
        // [LOG] Method completed in: 2ms
        
        User user = proxyService.getUser(1);
        // Output:
        // [LOG] Calling method: getUser
        // [LOG] Arguments: [1]
        // Getting user: 1
        // [LOG] Method completed in: 5ms
    }
}

От чего зависит Dynamic Proxy

От интерфейса

  • Proxy может быть создан только для интерфейса
  • Если класс не реализует интерфейс, нельзя создать proxy
  • Должны быть все методы интерфейса (toString, hashCode, equals будут перехватываться)

От ClassLoader

  • Proxy класс загружается в пакет com.sun.proxy.$Proxy0
  • ClassLoader должен быть доступен (иначе ClassNotFoundException)
  • Разные ClassLoader'ы создают разные proxy классы

От InvocationHandler

  • От логики InvocationHandler зависит, что будет происходить
  • Один InvocationHandler может использоваться для нескольких proxy объектов
  • InvocationHandler содержит реальный объект

От метаданных Method

  • Method object содержит информацию о методе
  • Можно проверить аннотации, типы параметров, возвращаемый тип
  • Это позволяет выполнять разную логику для разных методов

Практические примеры использования

Логирование — перехватываем все вызовы

Проверка прав доступа — проверяем права перед вызовом метода

public class SecurityInvocationHandler implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) 
            throws Throwable {
        
        if (!hasPermission(method)) {
            throw new SecurityException("Access denied");
        }
        return method.invoke(target, args);
    }
}

Кэширование — кэшируем результаты методов

public class CachingInvocationHandler implements InvocationHandler {
    private Map<String, Object> cache = new HashMap<>();
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        String key = method.getName() + Arrays.toString(args);
        if (cache.containsKey(key)) {
            return cache.get(key);
        }
        // ... выполняем метод и кэшируем результат
    }
}

Ограничения Dynamic Proxy

  • Работает только с интерфейсами — для классов нужны CGLIB или ByteBuddy
  • Final методы не проксируются — нельзя переопределить final
  • Performance — есть overhead на каждый вызов
  • Reflection — используется Reflection, что медленнее прямого вызова

Вывод

Dynamic Proxy зависит от:

  1. Интерфейса (must have)
  2. ClassLoader (загружает proxy класс)
  3. InvocationHandler (логика перехвата)
  4. Method метаданных (информация о методе)

Этот механизм используется в Spring (транзакции, безопасность), Hibernate (lazy loading), и других фреймворках для перехвата вызовов методов.