Spring Ehcache3导致带有key-type和value-type的异常

时间:2017-08-17 13:55:50

标签: java spring ehcache jcache

我尝试在4.3版本的项目中使用ehcache3。 我配置了缓存管理器:

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

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>

但是当我部署项目时,我有一个例外:

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

如果我删除:

<key-type>java.lang.Long</key-type>

它的工作正常,但缓存的keyType是Object, 需要做什么,我可以使用自己的密钥类型和值类型?

3 个答案:

答案 0 :(得分:4)

Spring cache is not typed,所以它没有使用Jcache (javax.cache / JSR-107 caching API)

的类型化API

既然你在ehcache.xml中指定了类型,那么Ehcache拒绝让Spring使用getCache()的非类型签名

当你考虑它时,如果你让Spring使用Ehcache(via @CacheResult and other JCache annotations for example),你必须让它为你选择什么是关键和值类型 - 它不再是你应该指定的人类型。

答案 1 :(得分:1)

正如您在org.springframework.cache.jcache.JCacheCacheManager的源代码中所看到的,Spring并不了解它应该使用方法getCache(String,Class,Class)而不是简单的getCache(String)。更确切地说,这个类对getCache(String,Class,Class)一无所知。

所以你有三种方式:

  1. 在get和put操作期间不执行任何操作缓存使用equals(),并且可能是来自密钥的真实类的hashCode()方法。如果您使用直接访问缓存而不是通过注释进行声明式访问,那么只有在显式类型转换中才会感到不适。

  2. 扩展此类并研究它以了解这些缓存配置功能。<​​/ p>

  3. 查看可能知道这些设置的另一个CacheManager。

答案 2 :(得分:0)

好的,你必须要破解一下:

编写自定义CacheManager并在配置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>

这是一些(伪)代码:

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;
            }
        }
    }

}
祝你好运。