#spring #spring-cache
#spring #spring-cache
Вопрос:
После обновления нашего spring до версии 4 наш пользовательский генератор ключей перестал работать. Перед переносом был выполнен наш код, переопределяющий метод «generate», но после перехода на spring 4.0.5 код вообще не выполняется. Вместо этого я увидел, что всегда выполняется SimpleKeyGenerator. Это ошибка spring? Почему я не могу переопределить метод 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);
}
}
Дополнительная информация: После отладки кода я увидел, что каждый раз, когда вызывается метод «generate», он выполняется ТОЛЬКО в SimpleKeyGenerator, а НЕ в моем пользовательском классе CacheKeyGenerator. Я попытался понять, почему, поэтому произвел некоторую отладку. Во время отладки я увидел, что существует org.springframework.cache.interceptor.Класс CacheAspectSupport, у которого есть частное свойство: private KeyGenerator KeyGenerator = new SimpleKeyGenerator(); У этого класса есть метод установки в свойстве KeyGenerator, и я увидел, что при запуске контекста этот метод установки вызывается с помощью моего пользовательского CacheKeyGenerator, поэтому я прихожу к выводу, что моя конфигурация правильная и проблема не в конфигурации. Я также видел, что при необходимости генерации ключей свойство KeyGenerator «потеряло» значение «CacheKeyGenerator» и имеет значение «SimpleKeyGenerator». Это объясняет, почему мой пользовательский код никогда не выполняется, но я не понимаю, почему свойство KeyGenerator указывает на SimpleKeyGenerator . Похоже на ОШИБКУ SPring. В чем проблема?
Ответ №1:
Просмотрев пример кода, который вы отправили по электронной почте, я вижу, что у вас две проблемы:
@EnableCaching
@EnableCaching
Аннотация предназначена для использования в качестве замены конфигурации XML при использовании Java config. Например:
@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
где кэширование не применяется.
Поскольку вам, похоже, действительно не нужно разделение root / web, я бы рекомендовал просто создать один 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);
}
У вас также гораздо больше шансов получить помощь с подобными проблемами, если вы можете публично предоставить пример кода, который воспроизводит проблему, и сделать его доступным в виде ссылки из stack overflow. GitHub — отличное место для размещения примеров проектов. Также, пожалуйста, не кричите, что это ошибка, пока не будете уверены 🙂
Комментарии:
1. Спасибо, я воспользуюсь вашим советом, но проблема остается. Пожалуйста, ознакомьтесь с моей дополнительной информацией в исходном сообщении. Похоже на ошибку Springs.
2.
@Cacheable(key="#spEL")
этот ключ не использует KeyGenerator, как решить?3. полагаясь на метод toString() ключа — на что полагается spring, если ничего не указано и входные объекты не являются примитивами?