3

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

<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,我需要做什么,我可以使用自己的键类型和值类型?

4

3 回答 3

8

Spring缓存没有类型化,所以它没有使用Jcache的类型化API (javax.cache / JSR-107缓存API)

现在,由于您在 ehcache.xml 中指定了类型,Ehcache 拒绝让 Spring 使用 getCache() 的非类型签名

当您考虑它时,如果您让 Spring 使用 Ehcache(例如通过 @CacheResult 和其他 JCache 注释),您必须让它为您选择键和值类型 - 不再是您应该指定类型。

于 2017-08-17T23:04:53.400 回答
3

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

所以你有三种方法:

  1. 什么都不做,因为在 get 和 put 操作缓存使用 equals() 并且可能是来自您的键的真实类的 hashCode() 方法。如果您使用对缓存的直接访问而不是通过注释进行声明性访问,那么显式类型转换只会让人感到不适。

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

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

于 2017-08-17T14:50:50.437 回答
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;
            }
        }
    }

}

祝你好运。

于 2017-08-17T15:17:10.407 回答