Можно ли в Java создать прокси для класса A, используя другой класс B?

«Можно ли в Java создать прокси для класса A, используя другой класс B?» — вопрос из категории Паттерны, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Да, можно, но не напрямую через стандартный java.lang.reflect.Proxy. Для проксирования конкретных классов (а не интерфейсов) требуются библиотеки, такие как CGLIB или ByteBuddy, которые генерируют байткод во время выполнения.

Как это работает (на примере CGLIB):

  1. Создается подкласс целевого класса A.
  2. Вызовы методов этого подкласса перехватываются callback-ом (например, реализацией MethodInterceptor), который определен в вашем классе B.
  3. В этом callback-е вы можете добавить логику "до", "после" или вместо вызова оригинального метода.

Пример с CGLIB:

import net.sf.cglib.proxy.*;

// Целевой класс
class ServiceA {
    public void process() {
        System.out.println("Оригинальная логика ServiceA");
    }
}

// Обработчик (ваш класс B)
class LoggingInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("[Прокси] Вызов метода: " + method.getName());
        Object result = proxy.invokeSuper(obj, args); // Вызов оригинального метода
        System.out.println("[Прокси] Метод завершен.");
        return result;
    }
}

// Создание прокси
public class Demo {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(ServiceA.class); // Указываем класс для проксирования
        enhancer.setCallback(new LoggingInterceptor()); // Устанавливаем наш обработчик

        ServiceA proxy = (ServiceA) enhancer.create(); // Создаем экземпляр прокси
        proxy.process(); // Вызов будет перехвачен
        // Вывод:
        // [Прокси] Вызов метода: process
        // Оригинальная логика ServiceA
        // [Прокси] Метод завершен.
    }
}

Важные ограничения:

  • Нельзя проксировать final классы или final методы.
  • Конструктор родительского класса вызывается дважды (это особенность CGLIB).
  • Требует добавления внешней библиотеки в зависимости проекта.