Ответ
Да, при динамическом проксировании создается новый класс во время выполнения.
1. Механизм создания прокси-класса:
// 1. Исходный интерфейс
interface DatabaseService {
void save(String data);
String findById(int id);
}
// 2. Реализация
class DatabaseServiceImpl implements DatabaseService {
public void save(String data) {
System.out.println("Saving: " + data);
}
public String findById(int id) {
return "Data_" + id;
}
}
// 3. Создание прокси с логированием
DatabaseService proxy = (DatabaseService) Proxy.newProxyInstance(
DatabaseService.class.getClassLoader(),
new Class[]{DatabaseService.class},
(p, method, args) -> {
long start = System.nanoTime();
System.out.println("[LOG] Start: " + method.getName());
try {
// Вызов оригинального метода
return method.invoke(new DatabaseServiceImpl(), args);
} finally {
long time = System.nanoTime() - start;
System.out.println("[LOG] End: " + method.getName() + ", time: " + time + "ns");
}
}
);
// 4. Проверка типа
System.out.println("Proxy class: " + proxy.getClass().getName());
// Вывод: com.sun.proxy.$Proxy0
System.out.println("Is DatabaseService: " + (proxy instanceof DatabaseService)); // true
System.out.println("Is DatabaseServiceImpl: " + (proxy instanceof DatabaseServiceImpl)); // false
2. Что происходит под капотом:
- JVM генерирует байткод нового класса.
- Класс реализует указанные интерфейсы.
- Каждый метод делегирует вызов
InvocationHandler.invoke(). - Класс кешируется для повторного использования.
3. CGLIB для проксирования классов:
// Пример с Spring (упрощенно)
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class); // Наследуем от КЛАССА
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before: " + method.getName());
return proxy.invokeSuper(obj, args);
}
});
UserService cglibProxy = (UserService) enhancer.create();
// cglibProxy - подкласс UserService
4. Сравнение подходов:
| Критерий | Java Dynamic Proxy | CGLIB/ByteBuddy |
|---|---|---|
| Тип цели | Только интерфейсы | Классы (не final) |
| Наследование | Реализация интерфейсов | Наследование класса |
| Производительность | Быстрее создание, медленнее вызов | Медленнее создание, быстрее вызов |
| Ограничения | Только public методы | Не работает с final классами/методами |
5. Практические применения:
- Транзакционность:
@Transactionalв Spring - Ленивая загрузка: Hibernate lazy loading
- Кеширование:
@Cacheable - Валидация: Перехват вызовов для проверки аргументов
- Метрики: Замер времени выполнения методов