Коли Spring створює proxy?
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
Реальний сценарій: зайвий проксі
Кожен проксі — додатковий об'єкт в пам'яті і невеликий overhead на виклик (порядку наносекунд). При великій кількості бінів це підсумовується.
Рішення: прибрали @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. Що таке proxy в Spring]]
- [[15. Що таке AOP (Aspect-Oriented Programming)]]
- [[17. Що робить анотація @Transactional]]