Статьи

Spring 3.1: кеширование и EhCache

Если вы посмотрите в Интернете примеры использования встроенного кэширования в Spring 3.1, вы, как правило, SimpleCacheManager на SimpleCacheManager , который, по словам парней в Spring, «полезен для тестирования или простых объявлений кэширования». Я на самом деле предпочитаю думать о SimpleCacheManager как о легком, а не простом; полезно в тех ситуациях, когда требуется небольшой кэш-память для каждой виртуальной машины Java. Если бы «Ребята в Spring» управляли супермаркетом, то SimpleCacheManager был бы в линейке продуктов «Основы» своего бренда.

Если, с другой стороны, вам нужен мощный кэш, который можно масштабировать, сохранять и распределять, тогда Spring также поставляется со встроенной оболочкой ehCache .

Хорошая новость заключается в том, что переключаться между реализациями Spring в кэшировании легко. Теоретически все зависит от конфигурации, и, чтобы доказать правильность теории, я взял пример кода из своего блога Caching и @Cacheable и запустил его с помощью реализации EhCache.

Этапы настройки аналогичны описанным в моем последнем блоге Caching and Config , но вам все равно нужно указать:

1
<cache:annotation-driven />

… В вашем конфигурационном файле Spring для включения кэширования. Вам также нужно определить бин с идентификатором cacheManager , только в этот раз вы ссылаетесь на класс Spring EhCacheCacheManager вместо SimpleCacheManager .

1
2
3
<bean id='cacheManager'
    class='org.springframework.cache.ehcache.EhCacheCacheManager'
    p:cacheManager-ref='ehcache'/>

В приведенном выше примере демонстрируется конфигурация EhCacheCacheManager . Обратите внимание, что он ссылается на второй компонент с идентификатором « ehcache ». Это настроено следующим образом:

1
2
3
<bean id='ehcache' class='org.springframework.cache.ehcache.EhCacheManagerFactoryBean'
    p:configLocation='ehcache.xml'
    p:shared='true'/>

« ehcache » имеет два свойства: configLocation и shared .
configLocation ‘ — это необязательный атрибут, который используется для указания местоположения файла конфигурации ehcache. В моем тестовом коде я использовал следующий файл примера:

1
2
3
4
5
<?xml version='1.0' encoding='UTF-8'?>
<ehcache xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:noNamespaceSchemaLocation='http://ehcache.org/ehcache.xsd'>
    <defaultCache eternal='true' maxElementsInMemory='100' overflowToDisk='false' />
    <cache name='employee' maxElementsInMemory='10000' eternal='true' overflowToDisk='false' />
</ehcache>

… Который создает два кэша: кэш по умолчанию и один с именем «employee».

Если этот файл отсутствует, EhCacheManagerFactoryBean просто выбирает файл конфигурации ehcache по умолчанию: ehcache-failsafe.xml , который находится в jar-файле ehcache-core .

EhCacheManagerFactoryBean атрибут EhCacheManagerFactoryBean является « shared ». Предполагается, что это необязательно, поскольку в документации указывается, что она определяет, «должен ли EHCache CacheManager использоваться совместно (как единое целое на уровне виртуальной машины) или быть независимым (обычно локальным в приложении). По умолчанию установлено значение «false», что создает независимый экземпляр ». Однако, если для этого параметра установлено значение false, вы получите следующее исключение:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.cache.interceptor.CacheInterceptor#0': Cannot resolve reference to bean 'cacheManager' while setting bean property 'cacheManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cacheManager' defined in class path resource [ehcache-example.xml]: Cannot resolve reference to bean 'ehcache' while setting bean property 'cacheManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'ehcache' defined in class path resource [ehcache-example.xml]: Invocation of init method failed; nested exception is net.sf.ehcache.CacheException: Another unnamed CacheManager already exists in the same VM. Please provide unique names for each CacheManager in the config or do one of following:
1. Use one of the CacheManager.create() static factory methods to reuse same CacheManager with same name or create one if necessary
2. Shutdown the earlier cacheManager before creating new one with same name.
The source of the existing CacheManager is: InputStreamConfigurationSource [stream=java.io.BufferedInputStream@424c414]
 at org.springframework.beans.factory.support.BeanDefinitionValueResolver.
resolveReference(BeanDefinitionValueResolver.java:328)
 at org.springframework.beans.factory.support.BeanDefinitionValueResolver.
resolveValueIfNecessary(BeanDefinitionValueResolver.java:106)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.
applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1360)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.
populateBean(AbstractAutowireCapableBeanFactory.java:1118)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.
doCreateBean(AbstractAutowireCapableBeanFactory.java:517)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.
createBean(AbstractAutowireCapableBeanFactory.java:456)
... stack trace shortened for clarity
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.
java:683)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.
run(RemoteTestRunner.java:390)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.
main(RemoteTestRunner.java:197)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cacheManager' defined in class path resource [ehcache-example.xml]: Cannot resolve reference to bean 'ehcache' while setting bean property 'cacheManager'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'ehcache' defined in class path resource [ehcache-example.xml]: Invocation of init method failed; nested exception is net.sf.ehcache.CacheException: Another unnamed CacheManager already exists in the same VM. Please provide unique names for each CacheManager in the config or do one of following:
1. Use one of the CacheManager.create() static factory methods to reuse same CacheManager with same name or create one if necessary
2. Shutdown the earlier cacheManager before creating new one with same name.
The source of the existing CacheManager is: InputStreamConfigurationSource [stream=java.io.BufferedInputStream@424c414]
 at org.springframework.beans.factory.support.BeanDefinitionValueResolver.
resolveReference(BeanDefinitionValueResolver.java:328)
 at org.springframework.beans.factory.support.BeanDefinitionValueResolver.
resolveValueIfNecessary(BeanDefinitionValueResolver.java:106)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.
applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1360)
... stack trace shortened for clarity
 at org.springframework.beans.factory.support.AbstractBeanFactory.
getBean(AbstractBeanFactory.java:193)
 at org.springframework.beans.factory.support.BeanDefinitionValueResolver.
resolveReference(BeanDefinitionValueResolver.java:322)
 ... 38 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'ehcache' defined in class path resource [ehcache-example.xml]: Invocation of init method failed; nested exception is net.sf.ehcache.CacheException: Another unnamed CacheManager already exists in the same VM. Please provide unique names for each CacheManager in the config or do one of following:
1. Use one of the CacheManager.create() static factory methods to reuse same CacheManager with same name or create one if necessary
2. Shutdown the earlier cacheManager before creating new one with same name.
The source of the existing CacheManager is: InputStreamConfigurationSource [stream=java.io.BufferedInputStream@424c414]
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.
initializeBean(AbstractAutowireCapableBeanFactory.java:1455)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.
doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.
createBean(AbstractAutowireCapableBeanFactory.java:456)
 at org.springframework.beans.factory.support.AbstractBeanFactory$1
.getObject(AbstractBeanFactory.java:294)
 at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.
getSingleton(DefaultSingletonBeanRegistry.java:225)
 at org.springframework.beans.factory.support.AbstractBeanFactory.
doGetBean(AbstractBeanFactory.java:291)
 at org.springframework.beans.factory.support.AbstractBeanFactory.
getBean(AbstractBeanFactory.java:193)
 at org.springframework.beans.factory.support.BeanDefinitionValueResolver.
resolveReference(BeanDefinitionValueResolver.java:322)
 ... 48 more
Caused by: net.sf.ehcache.CacheException: Another unnamed CacheManager already exists in the same VM. Please provide unique names for each CacheManager in the config or do one of following:
1. Use one of the CacheManager.create() static factory methods to reuse same CacheManager with same name or create one if necessary
2. Shutdown the earlier cacheManager before creating new one with same name.
The source of the existing CacheManager is: InputStreamConfigurationSource [stream=java.io.BufferedInputStream@424c414]
 at net.sf.ehcache.CacheManager.assertNoCacheManagerExistsWithSameName(CacheManager.
java:521)
 at net.sf.ehcache.CacheManager.init(CacheManager.java:371)
 at net.sf.ehcache.CacheManager.
                     
                     (CacheManager.java:339)
 at org.springframework.cache.ehcache.EhCacheManagerFactoryBean.
afterPropertiesSet(EhCacheManagerFactoryBean.java:104)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.
invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1514)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.
initializeBean(AbstractAutowireCapableBeanFactory.java:1452)
 ... 55 more

… когда вы пытаетесь запустить кучу юнит-тестов.

Я думаю, что это сводится к простой ошибке Spring фабрики менеджеров ehcache, так как он пытается создать несколько экземпляров кэша, используя new() а не используя, как говорится в исключении, «один из статических методов фабрики CacheManager.create ()», который позволяет использовать повторно тот же CacheManager с тем же именем. Следовательно, мой первый тест JUnit работает нормально, но все остальные не пройдены.

Обидная строка кода:

1
this.cacheManager = (this.shared ? CacheManager.create() : new CacheManager());

Мой полный XML-файл конфигурации приведен ниже для полноты:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version='1.0' encoding='UTF-8'?>
  
  <!-- Switch on the Caching -->
   <cache:annotation-driven />
 
 <!-- Do the component scan path -->
 <context:component-scan base-package='caching' />
 
 <bean id='cacheManager' class='org.springframework.cache.ehcache.EhCacheCacheManager'
  p:cacheManager-ref='ehcache'/>
  
 <bean id='ehcache' class='org.springframework.cache.ehcache.EhCacheManagerFactoryBean'
     p:configLocation='ehcache.xml' 
     p:shared='true'/>
</beans>

При использовании ehcache единственными деталями конфигурации, которые следует учитывать, являются зависимости Maven. Это довольно просто, поскольку ребята из Ehcache объединили все различные банки ehcache в один модуль Maven POM . Этот модуль POM может быть добавлен в файл POM вашего проекта с использованием приведенного ниже XML:

1
2
3
4
5
6
7
<dependency>
        <groupId>net.sf.ehcache</groupId>
        <artifactId>ehcache</artifactId>
        <version>2.6.0</version>
        <type>pom</type>
        <scope>test</scope>
    </dependency>

Наконец, файлы Jar ehcache доступны как из репозиториев Maven Central, так и из Sourceforge:

01
02
03
04
05
06
07
08
09
10
11
12
<repositories>
        <repository>
            <id>sourceforge</id>
            <url>http://oss.sonatype.org/content/groups/sourceforge/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
    </repositories>

Приятного кодирования и не забудьте поделиться!

Ссылка: Spring 3.1: Caching и EhCache от нашего партнера по JCG Роджера Хьюза в блоге Captain Debug’s Blog .