Когда Spring создаёт прокси?
4. Не проксируйте final классы/методы 5. Минимизируйте количество прокси 6. AopUtils → для проверки прокси
🟢 Junior Level
Spring создаёт прокси, когда видит специальные аннотации:
| Аннотация | Что добавляет прокси |
|---|---|
| @Transactional | Управление транзакциями |
| @Async | Асинхронное выполнение |
| @Cacheable | Кэширование результатов |
| @PreAuthorize | Проверка прав доступа |
@Service
public class MyService {
@Transactional // ← Прокси для транзакций
public void save() { }
@Async // ← Прокси для асинхронности
public CompletableFuture<Void> process() { }
}
🟡 Middle Level
Функциональные триггеры
@Transactional:
Proxy → открывает транзакцию → вызывает метод → commit/rollback
@Async:
Proxy → передаёт задачу в TaskExecutor → возвращает Future сразу
@Cacheable:
Proxy → проверяет кэш → если есть → возвращает из кэша
→ если нет → вызывает метод → сохраняет в кэш
Scoped Proxies:
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
// → Прокси ищет актуальный бин в RequestContextHolder
Механизм создания
BeanPostProcessor.postProcessAfterInitialization():
→ AnnotationAwareAspectJAutoProxyCreator сканирует бин
→ Нашёл аннотацию → создаёт прокси
→ Прокси заменяет оригинальный бин в контексте
🔴 Senior Level
AnnotationAwareAspectJAutoProxyCreator
Главный BPP для создания прокси:
1. Сканирует все аннотации и @Aspect
2. Для каждого бина проверяет Pointcut
3. Если подходит → создаёт прокси
4. Прокси идёт дальше по цепочке BPP
Избежание лишних прокси
Прокси = overhead (память + CPU)
→ Не ставьте @Transactional на методы, которые только читают и не требуют консистентного снимка данных. Если транзакция нужна, используйте `readOnly = true` для оптимизации Hibernate (skip dirty checking).
→ Прокси создаётся на ВЕСЬ бин, даже если одна аннотация
Production Experience
Реальный сценарий: лишний прокси
Каждый прокси — дополнительный объект в памяти и небольшой оверхед на вызов (порядка наносекунд). При большом количестве бинов это суммируется.
Решение: убрали @Transactional с read методов
→ Меньше прокси, меньше overhead
Best Practices
- @Transactional → только где нужно
- @Async → осторожно, прокси на весь бин
- Scoped Proxy → для web scope в Singleton
- Не проксируйте final классы/методы
- Минимизируйте количество прокси
- AopUtils → для проверки прокси
Резюме для Senior
- Прокси создаётся при @Transactional, @Async, @Cacheable
- BPP → postProcessAfterInitialization
- Scoped Proxy → для web scope в Singleton
- Каждый прокси = память + CPU overhead
- final → не проксируется
- AnnotationAwareAspectJAutoProxyCreator → главный создатель
🎯 Шпаргалка для интервью
Обязательно знать:
- Прокси создаётся при наличии @Transactional, @Async, @Cacheable, @PreAuthorize
- Механизм: AnnotationAwareAspectJAutoProxyCreator (BeanPostProcessor) в postProcessAfterInitialization()
- Scoped Proxy (@Scope с proxyMode) — создаёт прокси для web scope в Singleton-бинах
- Каждый прокси = небольшая память + CPU overhead (наносекунды на вызов)
- Spring сканирует все аннотации и @Aspect, проверяет Pointcut, создаёт прокси
- @Transactional на read-методах без необходимости лишний overhead — используйте readOnly = true
Частые уточняющие вопросы:
- Какой компонент отвечает за создание прокси? → AnnotationAwareAspectJAutoProxyCreator (BPP)
- Когда создаётся Scoped Proxy? → При
@Scope(value="request", proxyMode=TARGET_CLASS) - Можно ли избежать создания прокси? → Да, не ставить аннотации где не нужны
- Как Spring решает, какой тип прокси создать? → proxy-target-class=true → CGLIB, false + есть интерфейс → JDK Proxy
Красные флаги (НЕ говорить):
- «Прокси создаётся для каждого бина» — только с AOP-аннотациями
- «@Transactional на read-методах всегда обязателен» — лишний overhead без нужды
- «Прокси добавляет заметную задержку» — наносекунды
- «final-классы проксируются через CGLIB» — не проксируются
Связанные темы:
- [[11. Какие scope существуют в Spring]]
- [[13. Что такое прокси в Spring]]
- [[15. Что такое AOP (Aspect-Oriented Programming)]]
- [[17. Что делает аннотация @Transactional]]