Spring оборачивает бины в прокси, чтобы работали @Transactional и @Cacheable. Какие практические следствия из этого вытекают?
Прокси перехватывает только внешние вызовы: this.method() внутри класса идёт мимо прокси — аннотация не сработает. CGLIB не проксирует final-классы и final-методы, JDK-прокси требует интерфейс. Приватные методы не перехватываются.
- Решение self-invocation — вынести метод в другой бин или внедрить самого себя
- Spring Boot по умолчанию использует CGLIB
- Перехват только public-методов (в новых версиях — и protected у CGLIB)
А ещё Прокси не видно в equals и getClass — сравнения могут удивить
Подробно
Прокси перехватывает только внешние вызовы, проходящие через ссылку на него. Вызов this.method() внутри класса идёт напрямую — отсюда классическая ловушка с @Transactional при self-invocation. Реализаций две: JDK dynamic proxy (нужен интерфейс) и CGLIB (наследование, поэтому final-классы и final-методы не проксируются). Смена интерфейса — признак адаптера, а не прокси.
Что хотят услышать
- Self-invocation.
- Ограничения CGLIB и JDK-прокси.
Частые ошибки
- Искать причину неработающего @Transactional в конфигурации, а не в self-invocation.
- Думать, что прокси меняет контракт бина.
Проверьте себя
Несколько верных вариантов. Подумайте, потом откройте ответ.
Показать варианты и ответ
- ✓Вызов аннотированного метода изнутри того же класса идёт мимо прокси, и аннотация не срабатывает
- ✓CGLIB-прокси не может проксировать final-классы и final-методы
- ✓JDK-прокси требует, чтобы у бина был интерфейс
- ✗Прокси меняет интерфейс бина, поэтому клиентам нужно приведение типа
Что спросят дальше
- Как обойти проблему self-invocation?
- Чем прокси Hibernate для ленивых ассоциаций похож на этот механизм?
Разбор темы
- Структурные паттерны
Adapter, Decorator, Proxy и чем они отличаются друг от друга, Facade, Composite, Bridge и Flyweight — на примерах java.io, java.lang.reflect.Proxy и Spring AOP.
Связанные вопросы
- Чем паттерн Proxy отличается от Decorator, если у обоих тот же интерфейс, что и у обёрнутого объекта? Middle
- Как работает @Transactional и почему вызов метода внутри того же класса (self-invocation) может не открыть транзакцию? Middle
- Когда Spring создаёт JDK dynamic proxy, а когда CGLIB-прокси? Middle
Разбор собеседования с sotto
После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.