Карта / Кэширование / @Cacheable в Spring

@Cacheable в Spring

@Cacheable — одна аннотация, которая превращает обычный метод в кэшируемый, без единой строчки кода вручную проверяющей кэш.

@Service
class UserService {
    @Cacheable("users")              // результат метода кэшируется под именем "users"
    User getUser(Long id) {
        return userRepository.findById(id).orElseThrow();   // выполнится только при первом вызове с этим id
    }
}

Первый вызов getUser(1) реально выполняет метод и сохраняет результат в кэш. Все следующие вызовы с тем же id возвращают значение из кэша, не выполняя тело метода вообще.

Копнуть глубже

Это пример паттерна Proxy (см. тему «Структурные (Adapter, Proxy)») — Spring оборачивает бин в прокси, который перехватывает вызов метода, проверяет кэш до реального вызова, и решает: вернуть из кэша или выполнить метод по-настоящему.

@CachePut — всегда выполняет метод и обновляет кэш (в отличие от @Cacheable, который пропускает выполнение при наличии значения в кэше):

@CachePut("users")
User updateUser(User user) {
    return userRepository.save(user);   // выполнится всегда, кэш обновится новым значением
}

@CacheEvict — удаляет значение из кэша:

@CacheEvict("users")
void deleteUser(Long id) {
    userRepository.deleteById(id);
}

Ключевая ловушка @Cacheable — оно не работает при вызове метода изнутри того же класса. Spring AOP создаёт прокси вокруг бина, и перехват срабатывает только через вызов снаружи, через сам прокси-объект — this.getUser(1) внутри того же класса обойдёт кэш стороной (та же причина, что у @Transactional — см. тему «Как работают прокси»).

🎤 Закрыл тему, если можешь объяснить:
• что делает `@Cacheable` и в чём разница с `@CachePut`/`@CacheEvict`;
• почему `@Cacheable` не работает при вызове метода изнутри того же класса (если дошёл до 2-го слоя).