Как создать Proxy для обычного Java-класса без использования Spring?

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

Ответ

Да, можно, используя стандартные средства Java или сторонние библиотеки. Выбор зависит от того, реализует ли целевой класс интерфейсы.

1. Для классов, реализующих интерфейсы: java.lang.reflect.Proxy Это стандартный API Java, который создает динамический прокси на лету.

import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;

interface Service {
    void execute();
}

class RealService implements Service {
    public void execute() {
        System.out.println("Выполняю реальную работу.");
    }
}

InvocationHandler handler = (proxy, method, args) -> {
    System.out.println("Перед вызовом метода: " + method.getName());
    Object result = method.invoke(new RealService(), args);
    System.out.println("После вызова метода: " + method.getName());
    return result;
};

Service proxyInstance = (Service) Proxy.newProxyInstance(
    Service.class.getClassLoader(),
    new Class[]{Service.class},
    handler
);
proxyInstance.execute();

2. Для классов без интерфейсов: CGLIB или ByteBuddy Эти библиотеки создают прокси путем наследования от целевого класса. Пример с CGLIB:

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;

class SimpleService {
    public void process() {
        System.out.println("Обработка...");
    }
}

MethodInterceptor interceptor = (obj, method, args, proxy) -> {
    System.out.println("Перехват вызова.");
    return proxy.invokeSuper(obj, args);
};

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(SimpleService.class);
enhancer.setCallback(interceptor);
SimpleService proxy = (SimpleService) enhancer.create();
proxy.process();

Ключевые ограничения:

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