SpringMVC+MyBatis - 9 Spring的EnCache(Shiro Cache的解决方案是基于这个文章的)

SpringMVC+MyBatis - 9 Spring的EnCache(Shiro Cache的解决方案是基于这个文章的)

If you look around the web for examples of using Spring 3.1’s built in caching then you’ll usually bump into Spring’s SimpleCacheManager , which the Guys at Spring say is “Useful for testing or simple caching declarations”. I actually prefer to think of SimpleCacheManager  as lightweight rather than simple; useful in those situations where you want a small in memory cache on a per JVM basis. If the Guys at Spring were running a supermarket then  SimpleCacheManager would be in their own brand ‘basics’ product range.

If, on the other hand, you need a heavy duty cache, one that’s scalable, persistent and distributed, then Spring also comes with a built in ehCache  wrapper.

The good news is that swapping between Spring's caching implementations is easy. In theory it’s all a matter of configuration and, to prove the theory correct, I took the sample code from my  Caching and @Cacheable  blog and ran it using an EhCache implementation.

The configuration steps are similar to those described in my last blog  Caching and Config  in that you still need to specify:

<cache:annotation-driven /> 

...in your Spring config file to switch caching on. You also need to define a bean with an id of  cacheManager , only this time you reference Spring’s  EhCacheCacheManager  class instead of  SimpleCacheManager .

<bean id="cacheManager"     
class="org.springframework.cache.ehcache.EhCacheCacheManager"
p:cacheManager-ref="ehcache"/>

The example above demonstrates an  EhCacheCacheManager  configuration. Notice that it references a second bean with an id of ' ehcache '. This is configured as follows:

<bean id="ehcache" 
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
p:configLocation="ehcache.xml"
p:shared="true"/>

" ehcache " has two properties:  configLocation  and  shared .

' configLocation ' is an optional attribute that’s used to specify the location of an ehcache configuration file. In my test code I used the following example file:
<?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>
...which creates two caches: a default cache and one named “employee”.

If this file is missing then the  EhCacheManagerFactoryBean  simply picks up a default ehcache config file:  ehcache-failsafe.xml , which is located in ehcache’s  ehcache-core  jar file.

The other  EhCacheManagerFactoryBean  attribute is ' shared '. This is supposed to be optional as the documentation states that it defines "whether the EHCache CacheManager should be shared (as a singleton at the VM level) or independent (typically local within the application). Default is 'false', creating an independent instance.” However, if this is set to false then you’ll get the following exception: 
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
...when you try to run a bunch of unit tests.

I think that this comes down to a simple bug Spring’s the ehcache manager factory as it’s trying to create multiple cache instances using new()  rather than using, as the exception states, “one of the CacheManager.create() static factory methods" which allows it to reuse same CacheManager with same name. Hence, my first JUnit test works okay, but all others fail.


The offending line of code is:

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

My full XML config file is listed below for completeness:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
  xmlns:p="http://www.springframework.org/schema/p"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:cache="http://www.springframework.org/schema/cache" 
  xmlns:context="http://www.springframework.org/schema/context"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
     http: // www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-3.1.xsd
        http: // www.springframework.org/schema/cache  http://www.springframework.org/schema/cache/spring-cache.xsd ">
 
  <!-- 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>

文章摘自  http://www.captaindebug.com/2012/09/spring-31-caching-and-ehcache.html#.U8FilfmSxZW

你可能感兴趣的:(SpringMVC+MyBatis - 9 Spring的EnCache(Shiro Cache的解决方案是基于这个文章的))