🔖 Тайна игнорируемых Spring-аннотаций
Удивительно, как работают аннотации Spring вроде
@Transactional, @Cacheable, @Async. Но что происходит, когда они внезапно «отказываются» работать?Проблема кэширования
При создании службы уведомлений по email мы решили кэшировать дорогостоящую генерацию шаблонов с помощью
@Cacheable. Но в тестах обнаружилось: кэширование не работает! Метод выполнялся заново при каждом вызове, словно аннотация невидима для Spring.Секрет прокси
Изучив документацию Spring, мы узнали главное: функционал аннотаций поддерживается через прокси — специальные объекты-обертки, перехватывающие вызовы методов. Но есть важный нюанс: прокси не перехватывают внутренние вызовы.
Когда метод с
@Cacheable вызывается напрямую из другого метода того же класса, он выполняется в обход прокси. Отсюда и проблема с кэшированием.Решение через разделение
Проблема решается просто — вынесением кэшируемого метода в отдельную службу. Теперь вызов проходит через прокси, и кэширование работает как ожидалось.
Два типа прокси
В Spring используются два вида прокси:
Динамические прокси JDK реализуют тот же интерфейс и делегируют вызовы. Применяются по умолчанию для классов с интерфейсами.
Прокси CGLIB создают подкласс целевого класса и переопределяют методы. Используются для конкретных классов без интерфейсов.
Выбором прокси можно управлять через конфигурацию, но обычно стандартного поведения достаточно.
Главное — помнить: Spring-аннотации работают только через прокси. Избегайте внутренних вызовов, выносите функционал в отдельные компоненты, используйте только публичные методы.
Понимание механизма прокси Spring сэкономит часы отладки при работе с транзакциями, кэшированием и асинхронными операциями.
📎 Статья
🎙 Новости
📝 База вопросов
