Spring Ehcache3 cause exception with with key-type and value-type

守給你的承諾、 提交于 2019-12-22 12:36:27

问题


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:

  1. 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.

  2. Extends this class and study it to understand these cache config features.

  3. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!