Генерация ключей кэша Spring 4 не работает должным образом

После обновления нашей весны до версии 4 наш генератор пользовательских ключей перестал работать. До миграции наш код, переопределяющий метод «генерировать», выполнялся, но после перехода на spring 4.0.5 код вообще не выполняется. Вместо этого я увидел, что SimpleKeyGenerator всегда выполняется. Это баг весной? Почему я не могу переопределить метод generate своим собственным кодом, как это было в предыдущих версиях?

пример из корневого контекста:

<cache:annotation-driven key-generator="cacheKeyGenerator" />
<bean id="cacheKeyGenerator" class="com.poalim.xp.general.cache.CacheKeyGenerator"/>

образец из генерации ключа Java (до миграции)

public class CacheKeyGenerator extends DefaultKeyGenerator implements ApplicationContextAware {
public Object generate(Object target, Method method, Object... params) { 
    return  method.getName() + super.generate(target, method, params); 
}

}

пример кода после миграции

public class CacheKeyGenerator extends SimpleKeyGenerator implements ApplicationContextAware {
public Object generate(Object target, Method method, Object... params) { 
    return  method.getName() + super.generate(target, method, params); 
}

}

Дополнительная информация: после отладки кода я увидел, что каждый раз, когда вызывается метод «сгенерировать», он выполняется ТОЛЬКО в SimpleKeyGenerator, а НЕ в моем пользовательском классе CacheKeyGenerator. Я попытался понять, почему, поэтому я сделал некоторую отладку. Во время отладки я увидел, что есть класс org.springframework.cache.interceptor.CacheAspectSupport, который имеет приватное свойство: private KeyGenerator keyGenerator = new SimpleKeyGenerator(); Этот класс имеет метод установки для свойства keyGenerator, и я видел, что при запуске контекста этот метод установки вызывается с моим пользовательским CacheKeyGenerator, поэтому я делаю вывод, что моя конфигурация правильная и проблема не в конфигурации. Я также видел, что когда требуется генерация ключей, свойство keyGenerator «теряет» значение «CacheKeyGenerator» и имеет «SimpleKeyGenerator». Это объясняет, почему мой пользовательский код никогда не выполняется, но я не понимаю, почему свойство keyGenerator указывает на SimpleKeyGenerator. Это похоже на SPring BUG. В чем проблема?


person user3812776    schedule 07.07.2014    source источник


Ответы (1)


Посмотрев пример кода, который вы отправили по электронной почте, я вижу, что у вас есть две проблемы:

@EnableCaching

Аннотация @EnableCaching предназначена для использования в качестве замены конфигурации XML при использовании конфигурации Java. Например:

 @Configuration
 @EnableCaching
 public class AppConfig implements CachingConfigurer {

     @Bean
     @Override
     public CacheManager cacheManager() {
         // configure and return an implementation of Spring's CacheManager SPI
         SimpleCacheManager cacheManager = new SimpleCacheManager();
         cacheManager.addCaches(Arrays.asList(new ConcurrentMapCache("default")));
         return cacheManager;
     }

     @Bean
     @Override
     public KeyGenerator keyGenerator() {
         // configure and return an implementation of Spring's KeyGenerator SPI
         return new MyKeyGenerator();
     }

 }

Поскольку вы используете конфигурацию XML:

<cache:annotation-driven key-generator="cacheKeyGenerator" />

В вашем коде не должно быть аннотаций @EnableCaching, так как они могут переопределить ваш XML (удалите их из FacadeImpl).

Переопределение сканирования компонентов

У вас есть конфигурации root-context.xml и servlet-context.xml, однако обе они сканируют один и тот же пакет. Конфигурация вашего кэша объявлена ​​в root-context.xml, поэтому бины в этом контексте применяют кеширование, однако бины дублируются (поскольку они снова сканируются) в вашем servlet-context.xml, где кэширование не применяется.

Поскольку вам, похоже, не нужно разделение корня и сети, я бы порекомендовал просто создать один файл ApplicationContext. Сделать это:

1) Удалите следующие строки из вашего web.xml

<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:root-context.xml</param-value>
</context-param>

<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

2) Импортируйте файл root-context.xml напрямую, добавив следующее в файл servlet-context.xml.

<beans:import resource="classpath:root-context.xml"/>

Совет

И последнее, что выглядит немного странно, это то, что вы полагаетесь на метод toString() ключа. У меня возникло бы искушение использовать класс SimpleKey непосредственно для ваших нужд:

public Object generate(Object target, Method method, Object... params) {
    return new SimpleKey(method.getName(), params);
}

У вас также гораздо больше шансов получить помощь по подобным проблемам, если вы можете публично предоставить пример кода, который воспроизводит проблему, и сделать его доступным в виде ссылки из переполнения стека. GitHub — отличное место для размещения примеров проектов. Также, пожалуйста, не кричите, что это ОШИБКА, пока не будете уверены :)

person Phil Webb    schedule 07.07.2014
comment
Спасибо, воспользуюсь вашим советом, но проблема осталась. Пожалуйста, смотрите мою дополнительную информацию в исходном сообщении. Похоже на Springs BUG. - person user3812776; 07.07.2014
comment
@Cacheable(key="#spEL") этот ключ не использует генератор ключей, как решить? - person Dreampie; 13.09.2017
comment
полагаясь на метод toString() ключа - на что опирается Spring, если ничего не указано и объекты ввода не являются примитивами? - person Kalpesh Soni; 31.10.2017