【Ehcache技术专题】「入门到精通」带你一起从零基础进行分析和开发Ehcache框架的实战指南(8-Spring整合ehcache)

发布时间:2023年12月22日

系列文章目录

本系列课程主要针对于Ehcache缓存框架功能的开发实践全流程技术指南!

  • 第一节:Ehcache缓存框架的基本概念和简介
  • 第二节:Ehcache缓存框架的配置分析和说明
  • 第三节:Ehcache缓存框架的缓存方式的探索
  • 第四节:Ehcache缓存框架的配置分析和说明
  • 第五节:Ehcache缓存框架的查询分析和说明
  • 第六节:Ehcache缓存框架的监听器功能扩展
  • 第七节:Ehcache缓存框架的并发功能的开发
  • 第八节:Ehcache缓存框架的同步阻塞的开发
  • 第九节:Ehcache缓存框架的页面缓存的开发
  • 第十节:Ehcache缓存框架之结合Spring整合

回顾一下Ehcache

Ehcache是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider,广泛使用的开源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。它具有内存和磁盘存储,缓存加载器,缓存扩展,缓存异常处理程序,一个gzip缓存servlet过滤器,支持REST和SOAP api等特点。

Ehcache最初是由Greg Luck于2003年开始开发,2009年,该项目被Terracotta购买。软件仍然是开源,但一些新的主要功能(例如,快速可重启性之间的一致性的)只能在商业产品中使用,例如Enterprise EHCache and BigMemory。,维基媒体Foundationannounced目前使用的就是Ehcache技术。

主要的特性

  1. 快速
  2. 简单
  3. 多种缓存策略
  4. 缓存数据有两级:内存和磁盘,因此无需担心容量问题
  5. 缓存数据会在虚拟机重启的过程中写入磁盘
  6. 可以通过RMI、可插入API等方式进行分布式缓存
  7. 具有缓存和缓存管理器的侦听接口
  8. 支持多缓存管理器实例,以及一个实例的多个缓存区域
  9. 提供Hibernate的缓存实现

Spring框架所支持的第三方缓存

Spring引入了对Cache的支持。其使用方法和原理都类似于Spring对事务管理的支持。

Spring Cache是作用在方法上的,其核心思想是这样的:当我们在调用一个缓存方法时会把该方法参数和返回结果作为一个键值对存放在缓存中,等到下次利用同样的参数来调用该方法时将不再执行该方法,而是直接从缓存中获取结果进行返回。所以在使用Spring Cache的时候我们要保证我们缓存的方法对于相同的方法参数要有相同的返回结果。

spring的modules包中提供对许多第三方缓存方案的支持,包括:

  • EHCache
  • OSCache(OpenSymphony)
  • JCS
  • GigaSpaces
  • JBoss Cache

接下来,通过例举EHCache和OSCache详细介绍如何使用spring配置基于注解的缓存配置,注意这里的缓存是方法级的。将这些第三方缓存方案配置在spring中很简单,网上有许多介绍,这里只重点介绍如何配置基于注解的缓存配置。

Spring Cache的实现方式

Spring Cache需要我们做两方面的事:

  1. 声明某些方法使用缓存,基于注解的支持。
  2. 配置Spring对Cache的支持,它和Spring对事务管理的支持一样,Spring对Cache的支持也有基于注解和基于XML配置两种方式。

Spring Cache基本准备工作

定义Ehcache配置文件

<?xml version="1.0" encoding="UTF-8"?>  
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
    xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"  
    updateCheck="false" dynamicConfig="false" monitoring="autodetect">    
    <diskStore path="java.io.tmpdir" />  
    <!--  
        diskStore path:用来配置磁盘缓存使用的物理路径  
        name:   缓存名称,cache的唯一标识(ehcache会把这个cache放到HashMap里)  
        eternal="false"   元素是否永恒,如果是就永不过期(必须设置)  
        maxElementsOnDisk=磁盘缓存中最多可以存放的元素数量,0表示无穷大   
        maxElementsInMemory="1000" 内存缓存中最多可以存放的元素数量(必须设置)  
        timeToIdleSeconds="0"   导致元素过期的访问间隔(秒为单位). 0表示可以永远空闲,默认为0  
        timeToLiveSeconds="600" 元素在缓存里存在的时间(秒为单位). 0 表示永远存在不过期  
        overflowToDisk="false"  当缓存达到maxElementsInMemory值是,是否允许溢出到磁盘(必须设置)  
        diskPersistent="false"  磁盘缓存在VM重新启动时是否保持(默认为false)  
        diskExpiryThreadIntervalSeconds="100" 磁盘失效线程运行时间间隔,默认是120秒  
        memoryStoreEvictionPolicy="LFU" 内存存储与释放策略.当达到maxElementsInMemory时共有三种策略,分别为LRU(最近最少使用)、LFU(最常用的)、FIFO(先进先出)默认使用"最近使用"策略
-->  
    <defaultCache    
        eternal="false"  
        maxElementsInMemory="3000"    
        timeToIdleSeconds="3600"    
        timeToLiveSeconds="0"    
        overflowToDisk="true"    
        diskPersistent="false"    
        diskExpiryThreadIntervalSeconds="100"    
        memoryStoreEvictionPolicy="LRU"/>  
    <cache name="propConfigCache"    
        eternal="false"  
        maxElementsInMemory="3000"    
        overflowToDisk="true"  
        timeToIdleSeconds="0"    
        timeToLiveSeconds="1440"   
        memoryStoreEvictionPolicy="LFU"/>         
    <cache name="workTimeCache"    
        eternal="false"  
        maxElementsInMemory="3000"    
        overflowToDisk="true"  
        timeToIdleSeconds="0"    
        timeToLiveSeconds="1440"   
        memoryStoreEvictionPolicy="LFU"/>    
    <cache name="threeInOneCache"    
        eternal="false"  
        maxElementsInMemory="3000"    
        overflowToDisk="true"  
        timeToIdleSeconds="0"    
        timeToLiveSeconds="1440"   
        memoryStoreEvictionPolicy="LFU"/>   
    <cache name="transferCache"    
        eternal="false"  
        maxElementsInMemory="1000"    
        overflowToDisk="true"  
        timeToIdleSeconds="0"    
        timeToLiveSeconds="1440"   
        memoryStoreEvictionPolicy="LFU"/>     
    <cache name="threeInOneFavCache"    
        eternal="false"  
        maxElementsInMemory="3000"    
        overflowToDisk="true"  
        timeToIdleSeconds="0"    
        timeToLiveSeconds="1440"   
        memoryStoreEvictionPolicy="LFU"/>  
    <cache name="reserveTimeCache"    
        eternal="false"  
        maxElementsInMemory="3000"    
        overflowToDisk="true"  
        timeToIdleSeconds="0"    
        timeToLiveSeconds="1440"   
        memoryStoreEvictionPolicy="LFU"/>  
    <cache name="mqServerNameCache"    
        eternal="false"  
        maxElementsInMemory="3000"    
        overflowToDisk="true"  
        timeToIdleSeconds="0"    
        timeToLiveSeconds="1440"   
        memoryStoreEvictionPolicy="LFU"/>  
    <cache name="schWorkTimeCache"    
        eternal="false"  
        maxElementsInMemory="3000"    
        overflowToDisk="true"  
        timeToIdleSeconds="0"    
        timeToLiveSeconds="1440"   
        memoryStoreEvictionPolicy="LFU"/>  
   <!--  
	<diskStore path="java.io.tmpdir" />
 	-->  
    <diskStore path="E:/cachetmpdir"/>  
    <defaultCache maxElementsInMemory="10000" eternal="false"  
        timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"  
        maxElementsOnDisk="10000000" diskPersistent="false"  
        diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU" />  
    <cache name="andCache" maxElementsInMemory="10000"  
        maxElementsOnDisk="1000" eternal="false" overflowToDisk="true"  
        diskSpoolBufferSizeMB="20" timeToIdleSeconds="300" timeToLiveSeconds="600"  
        memoryStoreEvictionPolicy="LFU" />          
</ehcache> 

启用Spring-Cache

XML风格的
xml代码

启用缓存注解功能,这个是必须的,否则注解不会生效,另外,该注解一定要声明在spring主配置文件中才会生效。

<cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true"/>  

spring自己的换管理器,这里定义了两个缓存位置名称 ,既注解中的value

<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">  
    <property name="caches">  
        <set>  
            <bean  
                class="org.springframework.cache.concurrent.ConcurrentCacheFactoryBean"  
                p:name="default" />  
            <bean  
                class="org.springframework.cache.concurrent.ConcurrentCacheFactoryBean"  
                p:name="andCache" />  
        </set>  
    </property>  
</bean>

另外还可以指定一个 key-generator,即默认的key生成策略。接下来说说对ehcache的支持,其实只需要把cacheManager换成EHCache的cacheManager即可,如下:

cacheManager工厂类,指定ehcache.xml的位置:

<bean id="cacheManagerFactory" class="org.springframework.cache.ehcache.EhCacheManagerF	actoryBean"  p:configLocation="classpath:/config/ehcache.xml" />   
<!-- 声明cacheManager -->  
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"   
  			  p:cacheManager-ref="cacheManagerFactory" />  
JavaConfig注解风格
java代码
@Configuration  
@ComponentScan(basePackages = "com.xxx.xxx")  
@EnableCaching(proxyTargetClass = true)  
public class CacheConfiguration implements CachingConfigurer {  
    @Bean  
    @Override  
    public CacheManager cacheManager() {  
  
        try {  
            net.sf.ehcache.CacheManager ehcacheCacheManager  
                    = new net.sf.ehcache.CacheManager(new ClassPathResource("ehcache.xml").getInputStream());  
  
            EhCacheCacheManager cacheCacheManager = new EhCacheCacheManager(ehcacheCacheManager);  
            return cacheCacheManager;  
        } catch (IOException e) {  
            throw new RuntimeException(e);  
        }  
    }  
  
    @Bean  
    @Override  
    public KeyGenerator keyGenerator() {  
        return new SimpleKeyGenerator();  
    }  
}  
  1. 使用@EnableCaching启用Cache注解支持;
  2. 实现CachingConfigurer,然后注入需要的cacheManager和keyGenerator;从spring4开始默认的keyGenerator是SimpleKeyGenerator;

使用注解声明的方式

Spring为我们提供了几个注解来支持Spring Cache。其核心主要是@Cacheable和@CacheEvict。使用@Cacheable标记的方法在执行后Spring Cache将缓存其返回结果,而使用@CacheEvict标记的方法会在方法执行前或者执行后移除Spring Cache中的某些元素。下面我们将来详细介绍一下Spring基于注解对Cache的支持所提供的几个注解。

Spring缓存注解@Cacheable的用法

@Cacheable的定义

@Cacheable:负责将方法的返回值加入到缓存中

@Cacheable可以标记在一个方法上,也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。

  • 支持缓存的方法:Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。Spring在缓存方法的返回值时是以键值对进行缓存的,值就是方法的返回结果。
  • 缓存键处理,Spring又支持两种策略,默认策略自定义策略,这个稍后会进行说明。需要注意的是当一个支持缓存的方法在对象内部被调用时是不会触发缓存功能的。@Cacheable可以指定三个属性,value、key和condition。

不过,在实际开发中,我们往往是通过Spring的@Cacheable来实现数据的缓存的,所以,本文给大家详细介绍一下@Cacheable的用法。

@Cacheable支持的参数
  • value:缓存位置名称,不能为空,如果使用Ehcache,就是ehcache.xml中声明的cache的name
  • key:缓存的key,默认为空,既表示使用方法的参数类型及参数值作为key,支持SpEL
  • condition:触发条件,只有满足条件的情况才会加入缓存,默认为空,既表示全部都加入缓存,支持SpEL
@Cacheable的使用案例
//将缓存保存进andCache,并使用参数中的userId加上一个字符串(这里使用方法名称)作为缓存的key   
@Cacheable(value="andCache",key="#userId + 'findById'")  
public SystemUser findById(String userId) {  
    SystemUser user = (SystemUser) dao.findById(SystemUser.class, userId);        
    return user ;         
}  
//将缓存保存进andCache,并当参数userId的长度小于32时才保存进缓存,默认使用参数值及类型作为缓存的key  
@Cacheable(value="andCache",condition="#userId.length < 32")  
public boolean isReserved(String userId) {  
    System.out.println("hello andCache"+userId);  
   return false;  
}  

value属性指定Cache名称

value属性是必须指定的,其表示当前方法的返回值是会被缓存在哪个Cache上的,对应Cache的名称。其可以是一个Cache也可以是多个Cache,当需要指定多个Cache时其是一个数组。

   @Cacheable("cache1")//Cache是发生在cache1上的
   public User find(Integer id) {
      Return null;
   }
 
   @Cacheable({"cache1", "cache2"})//Cache是发生在cache1和cache2上的
   public User find(Integer id) {
      Return null;
   }
 
使用key属性自定义key

key属性是用来指定Spring缓存方法的返回结果时对应的key的。该属性支持SpringEL表达式。当我们没有指定该属性时,Spring将使用默认策略生成key。我们这里先来看看自定义策略,至于默认策略会在后文单独介绍。

自定义策略是指我们可以通过Spring的EL表达式来指定我们的key。这里的EL表达式可以使用方法参数及它们对应的属性。
使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。下面是几个使用参数作为key的示例。

  @Cacheable(value="users", key="#id")
   public User find(Integer id) {
      return null;
   }
 
   @Cacheable(value="users", key="#p0")
   public User find(Integer id) {
      return null;
   }
 
   @Cacheable(value="users", key="#user.id")
   public User find(User user) {
      return null;
   }
 
   @Cacheable(value="users", key="#p0.id")
   public User find(User user) {
      return null;

Spring缓存注解@CacheEvict的用法

@CacheEvict:负责清除缓存

@CacheEvict 支持如下几个参数
  • value:缓存位置名称,不能为空,同上
  • key:缓存的key,默认为空,同上
  • condition:触发条件,只有满足条件的情况才会清除缓存,默认为空,支持SpEL
  • allEntries:true表示清除value中的全部缓存,默认为false
@CacheEvict的使用案例
//清除掉指定key的缓存  
@CacheEvict(value="andCache",key="#user.userId + 'findById'")  
public void modifyUserRole(SystemUser user) {  
         System.out.println("hello andCache delete"+user.getUserId());  
}  
  
//清除掉全部缓存  
@CacheEvict(value="andCache",allEntries=true)  
public void setReservedUsers() {  
    System.out.println("hello andCache deleteall");  
}  

一般来说,我们的更新操作只需要刷新缓存中某一个值,所以定义缓存的key值的方式就很重要,最好是能够唯一,因为这样可以准确的清除掉特定的缓存,而不会影响到其它缓存值 。比如:我这里针对用户的操作,使用(userId+方法名称)的方式设定key值。

Spring缓存注解@CachePut的用法

应用到写数据的方法上,如新增/修改方法,调用方法时会自动把相应的数据放入缓存:

@CachePut的使用案例
@CachePut(value = "user", key = "#user.id")  
public User save(User user) {  
    users.add(user);  
    return user;  
}  

即调用该方法时,会把user.id作为key,返回值作为value放入缓存;

@CachePut注解
Java代码
public @interface CachePut {  
    String[] value();              //缓存的名字,可以把数据写到多个缓存  
    String key() default "";       //缓存key,如果不指定将使用默认的KeyGenerator生成,后边介绍  
    String condition() default ""; //满足缓存条件的数据才会放入缓存,condition在调用方法之前和之后都会判断  
    String unless() default "";    //用于否决缓存更新的,不像condition,该表达只在方法执行之后判断,此时可以拿到返回值result进行判断了  
}  
Java代码

除了这些默认的Cache之外,我们可以写自己的Cache实现;而且即使不用之后的Spring Cache注解,我们也尽量使用Spring Cache API进行Cache的操作,如果要替换底层Cache也是非常方便的。到此基本的Cache API就介绍完了,接下来我们来看看使用Spring Cache注解来简化Cache的操作。

Cache的运行流程

  1. 首先执行@CacheEvict(如果beforeInvocation=true且condition 通过),如果allEntries=true,则清空所有
  2. 接着收集@Cacheable(如果condition 通过,且key对应的数据不在缓存),放入cachePutRequests(也就是说如果cachePutRequests为空,则数据在缓存中)
  3. 如果cachePutRequests为空且没有@CachePut操作,那么将查找@Cacheable的缓存,否则result=缓存数据(也就是说只要当没有cache put请求时才会查找缓存)
  4. 如果没有找到缓存,那么调用实际的API,把结果放入result
  5. 如果有@CachePut操作(如果condition 通过),那么放入cachePutRequests
  6. 执行cachePutRequests,将数据写入缓存(unless为空或者unless解析结果为false);
  7. 执行@CacheEvict(如果beforeInvocation=false 且 condition 通过),如果allEntries=true,则清空所有

流程中需要注意的就是2/3/4步:

如果有@CachePut操作,即使有@Cacheable也不会从缓存中读取;问题很明显,如果要混合多个注解使用,不能组合使用@CachePut和@Cacheable;

文章来源:https://blog.csdn.net/l569590478/article/details/135146493
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。