← Назад к вопросам
Что такое прокси в Spring?
2.3 Middle🔥 191 комментариев
#Spring Boot и Spring Data#Spring Framework
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Прокси в Spring Framework
Прокси (Proxy) — это один из ключевых механизмов Spring, который позволяет добавлять дополнительную функциональность к объектам без их изменения. Это критально важно для реализации AOP, транзакций, кэширования и других cross-cutting concerns.
Что такое прокси-объект?
Прокси — это объект-посредник, который перехватывает вызовы методов оригинального объекта и добавляет дополнительное поведение:
// Оригинальный объект
@Service
public class UserService {
public void saveUser(User user) {
System.out.println("Пользователь сохранен");
}
}
// Spring создает прокси, который выглядит так:
public class UserService$$CGLIBProxy extends UserService {
@Override
public void saveUser(User user) {
// Логирование
System.out.println("[LOG] Вызов saveUser");
// Транзакция
beginTransaction();
// Вызов оригинального метода
super.saveUser(user);
// Завершение транзакции
commitTransaction();
}
}
Типы прокси в Spring
1. JDK Dynamic Proxy
Используется для интерфейсов. Работает с любым интерфейсом через Reflection API:
// Интерфейс
public interface UserService {
void saveUser(User user);
}
// Реализация
@Service
public class UserServiceImpl implements UserService {
@Override
public void saveUser(User user) {
System.out.println("User saved");
}
}
// Spring создает прокси, который имплементирует UserService
// UserService proxy = new UserServiceImpl(); // Прокси
Как это работает:
InvocationHandler handler = (proxy, method, args) -> {
System.out.println("До вызова метода");
Object result = method.invoke(userServiceImpl, args);
System.out.println("После вызова метода");
return result;
};
UserService proxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[]{UserService.class},
handler
);
Ограничения:
- Работает только с интерфейсами
- Не может перехватывать private методы
- Не может перехватывать static методы
2. CGLIB Proxy (Code Generation Library)
Используется для классов. Создает подкласс оригинального класса:
// Класс без интерфейса
@Service
public class OrderService {
public void processOrder(Order order) {
System.out.println("Order processed");
}
}
// Spring создает подкласс:
// public class OrderService$$CGLIBProxy extends OrderService { ... }
Как это работает:
- Spring генерирует подкласс в runtime
- Переопределяет методы для добавления функциональности
- Требует непустого конструктора
Ограничения:
- Не может перехватывать private методы
- Не может перехватывать final методы
- final классы не могут быть проксированы
- Медленнее JDK proxy при создании
Механизм AOP с прокси
// Аспект
@Aspect
@Component
public class TransactionAspect {
@Before("execution(* com.example.service.*.*(..))")
public void beginTransaction() {
System.out.println("BEGIN TRANSACTION");
}
@After("execution(* com.example.service.*.*(..))")
public void commitTransaction() {
System.out.println("COMMIT TRANSACTION");
}
}
// Сервис
@Service
public class PaymentService {
public void transfer(User from, User to, double amount) {
System.out.println("Transferring " + amount);
from.setBalance(from.getBalance() - amount);
to.setBalance(to.getBalance() + amount);
}
}
// Spring создает прокси:
// 1. Перехватывает вызов transfer()
// 2. Вызывает @Before (beginTransaction)
// 3. Вызывает оригинальный метод
// 4. Вызывает @After (commitTransaction)
Использование @Transactional
@Service
public class UserService {
@Transactional // Spring создает прокси для этого метода
public void createUser(User user) {
userRepository.save(user);
// Если выброс exception — rollback автоматически
}
}
// Spring создает что-то вроде:
public class UserService$$Proxy {
public void createUser(User user) {
Transaction tx = beginTransaction();
try {
userService.createUser(user); // оригинальный код
tx.commit();
} catch (Exception e) {
tx.rollback();
throw e;
}
}
}
Кэширование с прокси
@Service
public class UserService {
@Cacheable("users") // Spring создает прокси для кэширования
public User getUserById(Long id) {
System.out.println("Querying from database");
return userRepository.findById(id).orElse(null);
}
}
// Spring создает прокси, который:
// 1. Проверяет кэш
// 2. Если есть в кэше — возвращает из кэша
// 3. Если нет — вызывает оригинальный метод
// 4. Сохраняет результат в кэш
Проблемы с прокси
Проблема 1: Вызов через this
@Service
public class UserService {
@Transactional
public void method1() {
System.out.println("Method 1");
method2(); // ❌ Не будет использован прокси!
}
@Transactional
public void method2() {
System.out.println("Method 2");
}
}
// Решение: инжектировать себя или использовать ApplicationContext
@Service
public class UserService {
private final UserService self;
public UserService(UserService self) {
this.self = self;
}
@Transactional
public void method1() {
System.out.println("Method 1");
self.method2(); // ✅ Теперь используется прокси
}
}
Проблема 2: Private методы
@Service
public class UserService {
@Transactional
public void publicMethod() {
privateMethod(); // ❌ Прокси не перехватит!
}
@Transactional
private void privateMethod() {
// Эта аннотация не работает
}
}
Проблема 3: Вызов через интерфейс
// ✅ Правильно
UserService service = applicationContext.getBean(UserService.class);
service.saveUser(user); // Работает с прокси
// ❌ Неправильно
UserService service = new UserService();
service.saveUser(user); // Не работает с прокси
Выбор типа прокси
// Настройка в @EnableAspectJAutoProxy
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true) // true = CGLIB, false = JDK
public class AopConfig {
}
// Или через свойства приложения
spring.aop.proxy-target-class=true
Когда использовать:
- JDK Proxy: есть интерфейс (по умолчанию для интерфейсов)
- CGLIB: нет интерфейса или нужно проксировать конкретный класс
Таблица сравнения
| Характеристика | JDK Proxy | CGLIB Proxy |
|---|---|---|
| Требует интерфейс | Да | Нет |
| Работает с классами | Нет | Да |
| Скорость создания | Быстро | Медленнее |
| Скорость выполнения | Немного быстрее | Немного медленнее |
| Перехватывает private | Нет | Нет |
| Перехватывает final | Нет | Нет |