spring-ehcache 注解缓存

最原始的进行缓存的方式:spring

最原始的使用缓存的方式是经过一个全局map保存获取过的数据,下次获取数据时先从map中提取,若是有就直接返回,若是没有就从数据库中去读取,而后放入map中,固然,在作更新操做时须要同步更新这个map中的数据。这种方式虽然原始,可是在一些简单的场景下已经够用了,好比Java的类加载器就是使用的这种方式缓存加载过的class。数据库

经过ehcache以编程方式使用缓存:编程

跟上面的方式相同,可是缓存经过ehcache去管理,固然比使用map有N多种好处,好比缓存太大了快达到上限以后,将哪一部分缓存清除出去。这种方式彻底是经过代码的方式使用ehcache缓存,虽然自由,却也很麻烦;有些好比单纯的场景下,不须要如此麻烦,直接经过注解就好了。缓存

之前在Spring项目中要经过注解的方式使用缓存,好比借助一个jar包:ehcache-spring-annotations.jar,能够在googlecode上面下载,不过已经好久没有更新过了。svn

使用ehcache-spring-annotations.jarthis

Spring配置文件:google

Xml代码 spa

<?xml version= "1.0" encoding = "UTF-8"?>  
<beans xmlns= "http://www.springframework.org/schema/beans"  
      xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"  
      xmlns:ehcache= "http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring"  
      xsi:schemaLocation= "  
          http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
          http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring/ehcache-spring-1.1.xsd">  
   
      <ehcache:annotation-driven cache-manager ="ehcacheManager" />  
      <ehcache:config cache-manager = "ehcacheManager">  
           <ehcache:evict-expired-elements  
               interval= "60" />  
      </ehcache:config >  
      <bean id = "ehcacheManager"  
           class= "org.springframework.cache.ehcache.EhCacheManagerFactoryBean" >  
           <property name = "configLocation" value= "classpath:ehcache.xml" />  
      </bean >  
</beans>  

共有两个注解:设计

  • @Cacheabele      :指定方法使用缓存
  • @TriggersRemove  :从缓存中移除对象

在查询的方法上使用缓存:code

Java代码 

@Cacheable(cacheName="platform.config")  

在删除或更新的方法上清空缓存:

Java代码 

@TriggersRemove(cacheName="platform.config", when=When.AFTER_METHOD_INVOCATION , removeAll=true)  
public void updateConfig(SystemConfig config) {  
     this.configDao.update(config);  
}  

 

对于这种方式,这里不作详细探讨。

在Spring 3.1之前,必须借助以上jar包才能支持以注解的方式使用缓存,可是从Spring 3.1开始,已经提供了原生的注解支持,固然也更增强大。

Spring 3.1及以上版本的原生注解支持

Spring配置文件:

Xml代码 

<?xml version= "1.0" encoding = "UTF-8"?>  
<beans xmlns= "http://www.springframework.org/schema/beans"  
      xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"  
      xmlns:cache= "http://www.springframework.org/schema/cache"  
      xsi:schemaLocation= "  
          http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd  
          http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">  
   
      <cache:annotation-driven cache-manager ="ehcacheCacheManager" />  
      <bean id = "ehcacheCacheManager" class= "org.springframework.cache.ehcache.EhCacheCacheManager"  
           p:cacheManager-ref= "ehcacheManager" />  
      <bean id = "ehcacheManager"  
           class= "org.springframework.cache.ehcache.EhCacheManagerFactoryBean" >  
           <property name = "configLocation" value= "classpath:ehcache.xml" />  
      </bean>  
</beans>  
 

 

Spring原生的缓存的注解共有四个:

  • @Cacheable  :应用到读取数据的方法上,便可缓存的方法,如查找方法:先从缓存中读取,若是没有再调用方法获取数据,而后把数据添加到缓存中
  • @CacheEvict :即应用到移除数据的方法上,如删除方法,调用方法时会从缓存中移除相应的数据
  • @CachePut   :应用到写数据的方法上,如新增/修改方法,调用方法时会自动把相应的数据放入缓存
  • @Caching    :上面三种注解配置方法时,一个方法只能使用三者之一。若是要使用多个,则须要使用@Caching

 

在查询的方法上使用缓存:

Java代码 

@Cacheable(value="platform.config" )  
public String querySingleConfigValue(String configKey) {}

指定key的生成规则:

Java代码 

@Cacheable(value="platform.config", value="#configKey")  
public String querySingleConfigValue(String configKey) {}

缓存的key的生成:

若是在Cache注解上没有指定key的话,会使用KeyGenerator生成一个key,默认提供了DefaultKeyGenerator生成器(Spring4以后使用SimpleKeyGenerator)。

若是只有一个参数,就使用该参数做为key,不然使用SimpleKey做为key,好比有两个入参,那么key将是这样的形式:SimpleKey [DEVELOPER_NAME,chenfeng]。

能够在Cache注解上指定key的生成规则,经过SpEL表达式指定规则。详细能够参考:Spring Cache抽象详解

若是没有入参,又没有指定key,那么key将是“SimpleKey []”,这将很容易形成缓存的混乱,并且也不利于在更新的时候进行缓存的更新或移除。


下面的代码入参是:SimpleKey []

Java代码 

 
@Cacheable (value = PlatformPrivateConstants.CACHE_NAME_CONFIG)  
public List<SystemConfig> queryAllSingleConfigs() {  }

下面的代码入参是:SimpleKey [DEVELOPER_NAME,chenfeng]

Java代码 

@Cacheable (value = PlatformPrivateConstants.CACHE_NAME_CONFIG )  
public String querySingleConfigValue(String configKey, String defaultValue) {  }

在更新的方法上更新缓存:

Java代码 

@CachePut(value=PlatformPrivateConstants.CACHE_NAME_CONFIG, key="#config.configKey")  
public SystemConfig updateConfig(SystemConfig config) {  }

 经过"#config.configKey"指定以入参config对象的configKey属性值做为缓存的key。

更新的方法必须将要缓存的对象做为返回值,只有这个返回的对象才会放入缓存中,若是在返回值被返回以前篡改了其内部数据,也会被反映到缓存中。

指定正确的key以防止缓存混乱:

下面两个方法,前者经过configKey查询对象,后者经过configKey查询对象中的configValue属性值:

Java代码 

public SystemConfig querySingleConfig(String configKey) {  
public String querySingleConfigValue(String configKey) {  }

 若是不指定key,或者使用相同的key,好比:Java代码 

@Cacheable(value="platform.config")  
public SystemConfig querySingleConfig(String configKey) {  }
@Cacheable(value="platform.config")  
public String querySingleConfigValue(String configKey) {  }

若是以"DEVELOPER_NAME"做为configKey分别调用这两个方法,那么,先调用哪一个方法,则那个方法的返回值会以"DEVELOPER_NAME"做为key存入缓存中,此时调用这两个方法时都会从缓存中取得这个值。好比先调用了querySingleConfig方法,那么查询到的SystemConfig对象就会以"DEVELOPER_NAME"为key存入缓存中,而后调用querySingleConfigValue方法,由于仍会以"DEVELOPER_NAME"做为key从缓存中提取数据,这时就会从缓存中取得了刚才那个SystemConfig对象,却看成是字符串型的configValue属性值返回了。

因此必须为这两个方法指定不一样的缓存key:

Java代码 

@Cacheable(value="platform.config", key = "#configKey.concat('_object'))  
public SystemConfig querySingleConfig(String configKey) {  }
@Cacheable(value="platform.config")  
public String querySingleConfigValue(String configKey) {  }

 

这样前者使用入参configKey值后接"_object"做为key,然后者使用入参configKey值为key,就不会相互覆盖了。

在更新的方法上同时更新多个缓存:

在查询时,可能经过id查询也可能经过name查询,这样在缓存中就存有两份数据,在更新的时候就必须同时更新这两份缓存。

Java代码 

@Cacheable(value="platform.config", key = "#configId)  
public SystemConfig querySingleConfig(long configId) { } 
@Cacheable(value="platform.config", key = "#configKey)  
public SystemConfig querySingleConfig(String configKey) {  }

 

经过@Caching注解组合多个@CachePut注解,从而在调用更新方法时同时更新多份缓存(以不一样的key存在于缓存中)

Java代码 

@Caching(put={@CachePut(value="platform.config" , key="#config.configId"),  
              @CachePut(value="platform.config" , key="#config.configKey" )})  
public SystemConfig updateConfig(SystemConfig config) {  }

在更新方法上同时更新和清除缓存:

上面的方法虽然能够同时更新多个缓存,可是必须保证这些缓存的值是彻底同样的,好比上面两个方法缓存的都是SystemConfig对象,而对于下面的方法就无能为力了,由于下面的方法缓存的是SystemConfig对象的configValue属性值,而上面的方式只能把做为返回值的SystemConfig对象更新到缓存中。

Java代码 

@Cacheable(value="platform.config")  
public String querySingleConfigValue(String configKey) {  }

 

对于这种状况,经过@CacheEvict清除那些没法更新的缓存,这样下次获取不到这些缓存就会越过缓存从数据库查询最新的数据。

Java代码 

@Caching(evict={@CacheEvict(value="platform.config", key="#config.configKey" )},  
         put={@CachePut(value="platform.config", key="#config.configId.concat('_object')"),  
              @CachePut(value="platform.config", key="#config.configKey.concat('_object')" )})  
public SystemConfig updateConfig(SystemConfig config) {  }

使用缓存的业务层的设计宗旨:

使用缓存的业务类应尽可能精简、方法应尽量少,只保留最紧要的方法。

由于进行更新操做时须要考虑每个的获取数据的方法,必须将可能被更新操做波及的全部缓存进行更新或清除,若是获取数据的方法过多,这个工做量将变得很大,而且很容易出错,好比忘记了更新或清除某一份缓存,而大型项目中这种错误可能难以被发现。另外,保存多份缓存,也增长了ehcache的负担,由于它须要维护这些缓存数据,好比须要判断哪些缓存最久未使用而从缓存中移除等。

因此应该在处于低层的业务类上使用缓存,而在高层的业务类上提供丰富的获取数据的方法,高层业务类全部操做均调用底层的业务类实现。

对于不会被频繁调用的方法,不要进行缓存,没有实际意义,还无谓增大ehcache的负担,而且浪费宝贵的内存。

相关文章
相关标签/搜索