Spring Ehcache3 вызывает исключение с типом ключа и типом значения

Я пытаюсь использовать 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. Что нужно сделать, чтобы я мог использовать собственный тип ключа и типы значений?


person Антон Покрамович    schedule 17.08.2017    source источник


Ответы (3)


Кэш Spring не типизирован, поэтому он не использует типизированный API Jcache (API кэширования javax.cache / JSR-107)

Теперь, когда вы указали типы в файле ehcache.xml, Ehcache отказался разрешить Spring использовать нетипизированную подпись getCache().

Если подумать, если вы позволите Spring использовать Ehcache (например, через @CacheResult и другие аннотации JCache), вы должны позволить ему выбрать за вас, какие типы ключа и значения - больше не вы должны указывать типы.

person Anthony Dahanne    schedule 17.08.2017
comment
Привет, так что, если мы используем spring.cache.jcache.config: classpath:ecache.xml и внутри XML мы просто удаляем тип ключа и тип значения? - person wwjdm; 22.05.2019

Как видно из исходников org.springframework.cache.jcache.JCacheCacheManager, Spring не понимает, что следует использовать метод getCache(String, Class, Class) вместо простого getCache(String). Точнее, этот класс ничего не знает о getCache(String, Class, Class).

Итак, у вас есть три пути:

  1. Ничего не делайте, так как во время операций get и put кэш использует методы equals() и может быть методами hashCode() из реального класса вашего ключа. Только дискомфорт в явном приведении типов, если вы используете прямой доступ к кешу вместо декларативного доступа через аннотации.

  2. Расширяет этот класс и изучает его, чтобы понять эти функции конфигурации кэша.

  3. Посмотрите на другой CacheManager, который может знать эти настройки.

person Donz    schedule 17.08.2017

Хорошо, вам нужно немного взломать:

напишите пользовательский 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>

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

}

удачи.

person dieter    schedule 17.08.2017