использование весеннего кеша только для чтения, как установить весенний кеш только для чтения

когда я использую весенний кеш с redis, я использую его в двух приложениях, одно для чтения и записи, другое только для чтения, как я могу настроить?

Я пытаюсь сделать так, но это не работает!

@Cacheable(value = "books", key = "#isbn", condition = "false")

Кто-нибудь может помочь?


person Tonney Bing    schedule 19.12.2015    source источник


Ответы (3)


Вы неправильно поняли назначение атрибута "condition" аннотации @Cacheable. Согласно документации...

Если true, метод кэшируется, если нет, он ведет себя так, как будто метод не кэширован, то есть выполняется каждый раз, независимо от того, какие значения находятся в кеше или какие аргументы используются.

Атрибут condition просто определяет, следует ли сначала обращаться к кешу (например, Redis) перед выполнением (потенциально дорогого) метода. Если condition оценивается как false, метод всегда будет выполняться, а результат впоследствии кэшируется.

Я предполагаю, что в приложении только для чтения вы хотите сначала обратиться к кешу, если значение не находится в кеше, затем выполните метод, однако НЕ кэшируйте результат. Это правильно?

Если это так, то вам нужно только указать атрибут unless вместо атрибута condition вот так...

@Cacheable(value="books", key="#isbn", unless="true")
void someBookMutatingOperation(String isbn, ...) { .. }

Если, однако, вы хотите вообще избежать вызова кэшируемого метода в приложении только для чтения (версии) и просто обращаться к кешу независимо от того, существует ли значение в кеше на самом деле или нет, тогда ваша проблема немного больше. сложный/трудный.

Абстракция кэша Spring работает на предпосылке, что если значение не находится в кеше, то оно вернет null, чтобы указать на промах кеша, за которым затем следует последующий вызов метода. Только когда кеш возвращает значение для указанного ключа (ов), можно избежать вызова метода.

Без пользовательского расширения (возможно, с использованием (дополнительных) перехватчиков АОП) невозможно избежать поведения OOTB.

Я не буду подробно останавливаться на этом более позднем методе, если только ваш вариант использования не требует этого.

Надеюсь это поможет.

person John Blum    schedule 22.12.2015
comment
Благодарность! Если же вы хотите избежать кэширования, то вызов метода только для чтения будет правильным. я использовал CacheManager позже. это может решить. но это не красивое решение - person Tonney Bing; 22.12.2015
comment
Cache.ValueWrapper extendInfo = cacheManager.getCache(слова).get(слово); - person Tonney Bing; 22.12.2015
comment
Прямое обращение к CacheManager в вашем коде — это НЕ то, что я имею в виду в отношении более позднего UC. - person John Blum; 29.12.2015
comment
ИМХО здесь должно быть написано unless = "true". - person Matthias; 13.07.2017
comment
@Matthias - да, вы правы, см. следующий ответ ниже. Я исправлю свой оригинальный/первый ответ. - person John Blum; 14.07.2017

@Тонни Бинг

Прежде всего, мои извинения за то, что ввел вас в заблуждение в моем предыдущем ответе...

Если условие оценивается как false, метод всегда будет выполняться, и результат впоследствии кэшируется.

Последняя часть НЕ соответствует действительности. На самом деле атрибут condition действительно предотвращает кэширование результата метода @Cacheable. Но ни атрибут condition, ни атрибут unless не предотвращают вызов метода службы @Cacheable.

Кроме того, мой пример кода выше был неправильным. Для атрибута unless необходимо установить значение true, чтобы предотвратить кэширование результата метода @Cacheable.

После повторного прочтения этого раздела в Справочном руководстве Spring я осознал свою ошибку и написал пример тестового класса для проверки "условного" кэширования Spring.

So...

Что касается вашего варианта использования в бизнесе, то, как я понимаю его на основе вашего исходного вопроса, а затем и вашего ответа на мой предыдущий ответ, у вас есть метод службы @Cacheable, который необходимо подавить вызов в приложении только для чтения, независимо от того, значение есть в кеше или нет! Другими словами, значение всегда должно извлекаться из кэша, а метод службы @Cacheable должен НЕ вызываться в режиме только для чтения.

Теперь, чтобы не засорять код вашего приложения ссылками на компоненты инфраструктуры Spring и, в частности, Spring CacheManager, это хороший пример «сквозной задачи» (поскольку может существовать несколько операций службы приложений на основе мутаций) и, следовательно, могут быть обработаны надлежащим образом с помощью АОП.

Я закодировал такой пример, удовлетворяющий вашим требованиям здесь.

Это автономный тестовый класс. Ключевые характеристики этого тестового класса включают в себя...

  1. Использование внешней конфигурации (посредством свойства app.mode.read-only System) для определения того, находится ли приложение в режиме только для чтения.

  2. Использование AOP и пользовательского аспекта для управления тем, разрешен ли последующий вызов точки соединения (т. е. метода службы @Cacheable) (нет, в контексте только для чтения). Кроме того, я соответствующим образом установил порядок, в котором Совет (а именно, совет на основе @Cacheable вместе с советом handleReadOnlyMode в Аспекте UseCacheExclusivelyInReadOnlyModeAspect) должен срабатывать на основе приоритета.

  3. Обратите внимание на аннотацию @Cacheable в сервисном методе...

    @Cacheable(value = "Factorials", unless = "T(java.lang.System).getProperty('app.mode.read-only', 'false')")
    public Long factorial(long number) { .. }
    

Вы можете увидеть предполагаемое поведение с операторами вывода System.err в тестовом классе.

Надеюсь это поможет!

person John Blum    schedule 30.12.2015

@Джон Блюм, спасибо! с Новым Годом. ваш ответ вдохновил меня, я прочитал часть исходного кода весеннего кеша. класс CacheInterceptor. класс CacheAspectSupport.

private Object execute(CacheOperationInvoker invoker, CacheOperationContexts contexts) {
        // Process any early evictions
        processCacheEvicts(contexts.get(CacheEvictOperation.class), true, ExpressionEvaluator.NO_RESULT);

        // Check if we have a cached item matching the conditions
        Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));

        // Collect puts from any @Cacheable miss, if no cached item is found
        List<CachePutRequest> cachePutRequests = new LinkedList<CachePutRequest>();
        if (cacheHit == null) {
            collectPutRequests(contexts.get(CacheableOperation.class), ExpressionEvaluator.NO_RESULT, cachePutRequests);
        }

        Cache.ValueWrapper result = null;

        // If there are no put requests, just use the cache hit
        if (cachePutRequests.isEmpty() && !hasCachePut(contexts)) {
            result = cacheHit;
        }

        // Invoke the method if don't have a cache hit
        if (result == null) {
            result = new SimpleValueWrapper(invokeOperation(invoker));
        }

        // Collect any explicit @CachePuts
        collectPutRequests(contexts.get(CachePutOperation.class), result.get(), cachePutRequests);

        // Process any collected put requests, either from @CachePut or a @Cacheable miss
        for (CachePutRequest cachePutRequest : cachePutRequests) {
            cachePutRequest.apply(result.get());
        }

        // Process any late evictions
        processCacheEvicts(contexts.get(CacheEvictOperation.class), false, result.get());

        return result.get();
    }

Я думаю, следует предотвратить выполнение cachePutRequest. если кеш не попал, чтобы вызвать тело метода @Cacheable и не кэшировать результат. используйте, если не предотвратит вызов метода. Это правильно?

person Tonney Bing    schedule 06.01.2016
comment
Да, используйте unless. Тем не менее, значение должно быть установлено на true. Я исправил свой оригинальный/первый ответ выше. Ваше здоровье. - person John Blum; 14.07.2017