问题
I try to use ehcache3
on project with spring 4.3.
I configured cache manager:
<cache:annotation-driven />
<bean id="cacheManager" class="org.springframework.cache.jcache.JCacheCacheManager">
<property name="cacheManager">
<bean class="org.springframework.cache.jcache.JCacheManagerFactoryBean">
<property name="cacheManagerUri" value="classpath:ehcache.xml"/>
</bean>
</property>
</bean>
And ehcache.xml:
<config
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns='http://www.ehcache.org/v3'
xmlns:jsr107='http://www.ehcache.org/v3/jsr107'
xsi:schemaLocation="
http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd
http://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd" >
<service>
<jsr107:defaults enable-statistics="true" enable-management="true"/>
</service>
<cache alias="customerSettings">
<key-type>java.lang.Long</key-type>
<expiry>
<none/>
</expiry>
<resources>
<heap>500</heap>
</resources>
</cache>
</config>
But when I deploy project, I have an exception:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cacheManager' defined in ServletContext resource [/WEB-INF/spring/root-context.xml]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Cache [customerSettings] specifies key/value types. Use getCache(String, Class, Class)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1628)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)
... 100 more
Caused by: java.lang.IllegalArgumentException: Cache [customerSettings] specifies key/value types. Use getCache(String, Class, Class)
at org.ehcache.jsr107.Eh107CacheManager.getCache(Eh107CacheManager.java:297)
at org.springframework.cache.jcache.JCacheCacheManager.loadCaches(JCacheCacheManager.java:105)
at org.springframework.cache.support.AbstractCacheManager.initializeCaches(AbstractCacheManager.java:61)
at org.springframework.cache.support.AbstractCacheManager.afterPropertiesSet(AbstractCacheManager.java:50)
at org.springframework.cache.jcache.JCacheCacheManager.afterPropertiesSet(JCacheCacheManager.java:97)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624)
... 107 more
If I remove:
<key-type>java.lang.Long</key-type>
It's work fine, but the keyType of cache is Object, What is need to do, that I can use own key type and value types?
回答1:
Spring cache is not typed, so it's not using the typed API of Jcache (javax.cache / JSR-107 caching API)
Now since you specified types in your ehcache.xml, Ehcache refused to let Spring use the non typed signature of getCache()
When you think about it, if you let Spring use Ehcache (via @CacheResult and other JCache annotations for example), you have to let it choose for you what are the key and value types - it's no longer you who should specify types.
回答2:
As you can see in sources of org.springframework.cache.jcache.JCacheCacheManager Spring doesn't understand that it should use method getCache(String, Class, Class) instead of simple getCache(String). More precisely this class doesn't know anything about getCache(String, Class, Class).
So you have three ways:
Do nothing as during get and put operations cache uses equals() and may be hashCode() methods from real class of your key. Only discomfort is in explicit type casting if you use direct access to cache instead of declarative access via annotations.
Extends this class and study it to understand these cache config features.
Look at another CacheManager that might know these settings.
回答3:
Ok you have to hack a little bit:
write a custom CacheManager
and use it in you configuration xml:
<bean id="cacheManager" class="your.path.MyCustomLongObjectJCacheManager">
<property name="cacheManager">
<bean class="org.springframework.cache.jcache.JCacheManagerFactoryBean">
<property name="cacheManagerUri" value="classpath:ehcache.xml"/>
</bean>
</property>
</bean>
Here is some (pseudo) code:
public class MyCustomLongObjectJCacheManager extends JCacheCacheManager{
@Override
protected Collection<Cache> loadCaches() {
javax.cache.CacheManager cacheManager = getCacheManager();
Collection<Cache> caches = new LinkedHashSet<Cache>();
for (String cacheName : getCacheManager().getCacheNames()) {
if("customerSettings".equals(cacheName)){ // or manager instance of Eh107CacheManager...
javax.cache.Cache<Long, Object> jcache = cacheManager.getCache(cacheName, Long.class, Object.class);
caches.add(new MyCustomAdaptingCache(jcache, isAllowNullValues()));
} else {
javax.cache.Cache<Object, Object> jcache = cacheManager.getCache(cacheName);
caches.add(new JCacheCache(jcache, isAllowNullValues()));
}
}
return caches;
}
@Override
protected Cache getMissingCache(String cacheName) {
// Check the JCache cache again (in case the cache was added at runtime)
javax.cache.CacheManager cacheManager = getCacheManager();
if("customerSettings".equals(cacheName)){
javax.cache.Cache<Long, Object> jcache = cacheManager.getCache(cacheName, Long.class, Object.class);
return new MyCustomAdaptingCache(jcache, isAllowNullValues());
}
javax.cache.Cache<Object, Object> jcache = getCacheManager().getCache(cacheName);
if (jcache != null) {
return new JCacheCache(jcache, isAllowNullValues());
}
return null;
}
}
public static class MyCustomAdaptingCache extends AbstractValueAdaptingCache {
private final javax.cache.Cache<Long, Object> cache;
public MyCustomAdaptingCache(javax.cache.Cache<Long, Object> jcache) {
this(jcache, true);
}
public MyCustomAdaptingCache(javax.cache.Cache<Long, Object> jcache, boolean allowNullValues) {
super(allowNullValues);
Assert.notNull(jcache, "Cache must not be null");
this.cache = jcache;
}
@Override
public final String getName() {
return this.cache.getName();
}
@Override
public final javax.cache.Cache<Long, Object> getNativeCache() {
return this.cache;
}
@Override
protected Object lookup(Object key) {
return this.cache.get((Long)key);
}
@Override
public <T> T get(Object key, Callable<T> valueLoader) {
try {
return this.cache.invoke((Long)key, new ValueLoaderEntryProcessor<T>(), valueLoader);
}
catch (EntryProcessorException ex) {
throw new ValueRetrievalException(key, valueLoader, ex.getCause());
}
}
@Override
public void put(Object key, Object value) {
this.cache.put((Long)key, toStoreValue(value));
}
@Override
public ValueWrapper putIfAbsent(Object key, Object value) {
boolean set = this.cache.putIfAbsent((Long)key, toStoreValue(value));
return (set ? null : get(key));
}
@Override
public void evict(Object key) {
this.cache.remove((Long)key);
}
@Override
public void clear() {
this.cache.removeAll();
}
private class ValueLoaderEntryProcessor<T> implements EntryProcessor<Long, Object, T> {
@SuppressWarnings("unchecked")
@Override
public T process(MutableEntry<Long, Object> entry, Object... arguments)
throws EntryProcessorException {
Callable<T> valueLoader = (Callable<T>) arguments[0];
if (entry.exists()) {
return (T) fromStoreValue(entry.getValue());
}
else {
T value;
try {
value = valueLoader.call();
}
catch (Exception ex) {
throw new EntryProcessorException("Value loader '" + valueLoader + "' failed " +
"to compute value for key '" + entry.getKey() + "'", ex);
}
entry.setValue(toStoreValue(value));
return value;
}
}
}
}
good luck.
来源:https://stackoverflow.com/questions/45737109/spring-ehcache3-cause-exception-with-with-key-type-and-value-type