# HOS-Cache 缓存

# 概述

缓存功能共分为两大类:HOS-Cache可切换缓存Redis缓存

HOS-Cache可切换缓存组件底层兼容Ehcache以及Redis, 可通过配置项完成不同的缓存库无缝切换, 并提供了注解工具类两种方式供开发人员使用,

  • 注解的使用方式:提供包括@HOSCacheable,@HOSCachePut,@HOSCacheEvict, @Caching,@HOSCacheConfig在内的五种注解,并兼容多租户功能
  • 工具类使用方式:提供 兼容多租户功能 与 原生功能(无特殊处理) 两种工具类, 开发人员通过调用工具类中提供的方法,完成对缓存的添加、更新、删除、设置过期时间的操作。

Redis缓存 请参考Redis使用说明

对比:HOS-CacheRedis缓存 均提供工具类的使用方式, 相比Redis缓存HOS-Cache底层兼容Ehcache以及Redis, 并提供注解的使用方式,另外HOS-Cache在使用时需要指定CacheName(缓存名称,为兼容Ehcache), Redis缓存则不需要

本文主要介绍 HOS-Cache可切换缓存

# 配置

# 导入maven依赖

<dependency>
    <groupId>com.mediway.hos</groupId>
    <artifactId>hos-framework-cache-starter</artifactId>
</dependency>

# 配置项

本功能相关的配置项位于application-dev.yml中,具体的位置如下图:

img.png

基础配置项

framework:
  hoscache:
    ##使用的缓存类型,目前支持 redis ehcache
    type: ehcache
    ##缓存前缀,用于在用同一个缓存服务时区分各个系统缓存
    prev: hos

通过framework.hoscache.type配置项,指定具体使用的缓存库,目前可用的值为redis与ehcache

framework.hoscache.cacheConfigureNames配置项中为缓存名称(cacheName)以及其过期时间等参数

framework.hoscache.cacheConfigureNames配置项已取消使用,现可通过缓存管理页面功能直接配置, 详情请参考 缓存管理


下面分别介绍关于Ehcahe和Redis的配置项,使用者可以根据使用的缓存库完善对应的配置项

Ehcache缓存库配置项:

framework:
  hoscache:
    # ehcache缓存位置
    ehcachePath: /temp

Redis缓存库配置项:

spring:
  redis:
    password: xxx #缓存库密码
    database: 0
    timeout: 5000 #连接超时时间(毫秒)
    #host: 127.0.0.1  #缓存库地址,单机模式下使用
    #port: 6379       #缓存库使用的接口,单机模式下使用
    #连接池相关配置,一般直接采用默认值,可以不写
    pool:
      max-activ: 8 #最大连接数(使用负值表示没有限制)
      max-wait: -1 #连接池最大阻塞等待时间(使用负值表示没有限制)
      max-idle: 8 #连接池中的最大空闲连接
      min-idle: 0 #连接池中的最小空闲连接
    #集群模式下的缓存库的ip和接口配置
    cluster:
      nodes: 192.16.18.196:7001,192.16.18.196:7002,192.16.18.196:7003,192.16.18.196:7004,192.16.18.196:7005,192.16.18.196:7006

# 功能简介

本插件包含 注解 与 工具类 两部分功能。功能介绍如下:

# 注解功能

支持 @HOSCacheConfig @HOSCacheable, @HOSCachePut, @HOSCacheEvict, @HOSCaching 五种注解。具体功能描述如下:


# @HOSCacheConfig

功能描述

用在类上,为 @HOSCacheable、@HOSCacheEvict、@HOSCachePut 的辅助注解,不使用可以不添加。 规定方法上使用的@HOSCacheable、@HOSCacheEvict、 @HOSCachePut中属性CacheNames的值。

若使用了该注解,方法上使用@HOSCacheable、@HOSCacheEvict、 @HOSCachePut注解中的属性CacheNames 可以不做单独声明,会默认使用此处的CacheNames。 但是,若类上@HOSCacheConfig中已经规定了CacheNames,方法上@HOSCacheable、@HOSCacheEvict、 @HOSCachePut 注解中单独声明了CacheNames,会以各自注解中的CacheNames值为准。

使用位置: 类

属性

属性 功能描述
cacheNames 缓存名称,必须指定至少一个

使用例子: 结合@HOSCacheable注解使用,此时@HOSCacheable中属性CacheNames值为 @HOSCacheConfig中规定的cacheNames, 即cahceTest002

@HOSCacheConfig(cacheNames = "cahceTest002")
public class AccountService01 {
  @HOSCacheable
  public String getAccount(String testUserName, int value2) {
    System.out.println("获取数据中。。1。。。。");
    return testUserName + String.valueOf(new Random().nextInt(100));
  }
}

# @HOSCacheable

功能描述

根据注解属性 condition 以及属性 unless ,判断是否进行缓存。判断通过后, 根据 注解属性key中的规则 生成缓存key, 并到缓存中查找数据,若存在数据,不执行方法直接返回数据。 若不存在数据,在方法执行完成后将方法的出参作为缓存值保存到缓存库中

使用位置: 方法

属性

属性 功能描述
cacheNames 缓存名称,若结合@HOSCacheConfig使用可以为空,否则,必须指定至少一个
key 缓存的 key,可以为空。如果指定,要按照 SpEL 表达式编写,如果不指定,则按照方法的所有入参自动组合生成。例如:@HOSCacheable(value=”testcache”,key=”#id”)
expireSecond 缓存失效时间,单位为秒,默认为持久缓存
condition 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存。例如:@HOSCacheable(value=”testcache”,condition=”#userName.length()>2”)
unless 否定缓存条件,使用 SpEL 编写,返回 true 或者 false。当条件结果为TRUE时,就不会缓存。@HOSCacheable(value=”testcache”,unless=”#userName.length()>2”)

Spel的规则请参考下面的描述

SpEL简单使用:

要指定 属性key、condition、unless 的值时须使用SpEL表达式,使用 '#'+参数名称 指定到对应的入参

例如:

使用缓存注解的方法: public String getAccount(String testUserName, int value2)

入参的实际值: testUserName->"user01" , value2->1121

若使用 testUserName+"-"+value2 的格式生成key,则写作 #testUserName+'-'+#value2, 最后生成的结果为user01-1121。 使用在condition和unless中支持基础的判断语句,例如 #value2>1000 ,即判断 value2 的值是否大于1000, 最后得出boolean结果(true或者false)

复杂的使用请自行百度

使用例子:使用@HOSCacheable注解,指定cacheNames,与condition判断条件(只有满足条件的才缓存)

    @HOSCacheable(cacheNames = "cahceTest002" ,condition="#value2 > 100")
    public String getAccount(String testUserName,int value2)

若只需要指定cacheNames,属性名称可以省略,例如

    @HOSCacheable("cahceTest002")
    public String getAccount(String testUserName,int value2)

key的默认生成规则:获取方法的所有入参,各个入参之间使用英文冒号(:)拼接。 例如:方法的所有入参为 "ceshi01",28,"山东" 三项,采用默认生成方式生成的key为 ceshi01:28:山东


# @HOSCachePut

功能描述

根据注解属性 condition 以及属性 unless ,判断是否进行缓存。判断通过后, 根据 注解属性key中的规则 生成缓存key,在方法执行完成后,根据缓存key到缓存库中查找数据,若存在数据, 将方法的出参作为缓存值更新到对应的缓存key中。若不存在,将方法的出参作为缓存值保存到缓存库中

使用位置: 方法

属性

属性 功能描述
cacheNames 缓存名称,若结合@HOSCacheConfig使用可以为空,否则,必须指定至少一个
key 缓存的 key,可以为空。如果指定,要按照 SpEL 表达式编写,如果不指定,则按照方法的所有入参自动组合生成。例如:@HOSCacheable(value=”testcache”,key=”#id”)
expireSecond 缓存失效时间,单位为秒,默认为持久缓存
condition 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存。例如:@HOSCacheable(value=”testcache”,condition=”#userName.length()>2”)
unless 否定缓存条件,使用 SpEL 编写,返回 true 或者 false。当条件结果为TRUE时,就不会缓存。@HOSCacheable(value=”testcache”,unless=”#userName.length()>2”)

使用例子:指定CacheNames并执行key的生成方式

    @HOSCachePut(cacheNames = "cahceTest001",key = "#testUserName")
    public String getAccount004(String testUserName)

若只需要指定cacheNames,属性名称可以省略, 例如:

    @HOSCachePut("cahceTest001")
    public String getAccount004(String testUserName)

该处cacheNames属性值为"cahceTest001"


# @HOSCacheEvict

功能描述

根据注解属性 condition 以及属性 unless ,判断是否进行缓存删除。判断通过后, 根据 注解属性key中的规则 生成缓存key,并到缓存库中查找数据, 若已存在,将缓存key以及对应的数据从缓存库中删除

使用位置: 方法

属性

属性 功能描述
cacheNames 缓存名称,若结合@HOSCacheConfig使用可以为空,否则,必须指定至少一个
key 缓存的 key,可以为空。如果指定,要按照 SpEL 表达式编写,如果不指定,则按照方法的所有入参自动组合生成。例如:@HOSCacheable(value=”testcache”,key=”#id”)
condition 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存。例如:@HOSCacheable(value=”testcache”,condition=”#userName.length()>2”)
unless 否定缓存条件,使用 SpEL 编写,返回 true 或者 false。当条件结果为TRUE时,就不会缓存。@HOSCacheable(value=”testcache”,unless=”#userName.length()>2”)
allEntries 是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空缓存空间(cacheNames)的缓存数据
beforeInvocation 是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存

使用例子: @HOSCacheEvict在方法上的使用,指定cacheNames并执行key的生成方式

    @HOSCacheEvict(cacheNames = "cahceTest001",key = "#testUserName")
    public String getAccount004(String testUserName)

若只需要指定cacheNames,属性名称可以省略, 例如:

    @HOSCacheEvict("cahceTest001")
    public String getAccount004(String testUserName)

该处cacheNames属性值为"cahceTest001"


# @HOSCaching

功能描述

在一个方法上同一类型的注解只能存在一个,为支持同时多种缓存处理的情况,提供该注解。 提供cacheable、put、evict属性分别用于存放@HOSCacheable、@HOSCachePut、@HOSCacheEvict注解。

使用位置: 方法

属性

属性 功能描述
cacheable 存放@HOSCacheable注解,可以为空
put 存放@HOSCachePut注解,可以为空
evict 存放@HOSCacheEvict注解,可以为空

使用例子: 在方法上的使用:

@HOSCaching(
    cacheable = {
        @HOSCacheable(value = "emp0")
    },
    put = {
        @HOSCachePut(value = "emp1",key = "#testUserName"),
        @HOSCachePut(value = "emp2",key = "#testUserName"),
    },
    evict = {
        @HOSCacheEvict(value = "emp3",key = "#testUserName")      
    }
)
public String getAccount001(String testUserName)

注意:以上提到的五种注解均不可使用到Controller控制层

# 工具类提供的方法以及功能描述

本插件提供了两个工具类:CacheTenantUtilCacheUtil

CacheTenantUtil工具类支持多租户功能,根据多租户开关自动将租户id拼接到key中.

CacheUtil工具类中无其他特殊处理,缓存key由开发者自定义

工具类介绍如下:

# CacheTenantUtil

缓存工具类CacheTenantUtil,其提供的方法如下

/**
 * 从缓存库中已存在的缓存数据
 * 
 * cacheName 缓存名称,不可为空
 * key  缓存键的参数,可传递多项,会根据默认key生成规则最终生成缓存key
 * 
  */    
public static Object get(String cacheName, Object... key)


/**
 * 向缓存库中存放数据
 * 
 * cacheName 缓存名称,不可为空
 * value 存入到缓存中的数据
 * key  缓存键的参数,可传递多项,会根据默认key生成规则最终生成缓存key
 * 
 */
public static void put(String cacheName, Object value,Object... key);


/**
 * 向缓存中存放数据,设置过期时间
 *
 * @param cacheName 缓存名称
 * @param value 向缓存中存放的数据
 * @param expireSecond 失效时间单位为秒
 * @param key   缓存key参数
 */
public static void putESecond(String cacheName, Object value,int expireSecond,Object... key);

/**
 * 删除缓存库中已存在的数据
 * 
 * cacheName 缓存名称,不可为空
 * key  缓存键的参数,可传递多项,会根据默认key生成规则最终生成缓存key
 * 
 */
public static void evict(String cacheName, Object... key)

/**
 * 清除缓存库中对应cacheName(缓存名称)下所有的缓存数据
 * 
 * @param cacheName 缓存名称,不可为空
 * 
 */
public static void clear(String cacheName)

/**
 * 获取过期时间,单位为秒
 *
 * @param cacheName 缓存名称
 * @param key 缓存键的参数,可传递多个,共同生成缓存key
 * @return
 */
public static long getExpire(String cacheName, Object... key)

/**
 * 根据 缓存名称 与 key 获取 缓存key列表
 *
 * @param cacheName 缓存名称
 * @param key   缓存键的参数,可传递多个,共同生成缓存key
 * @return @
 */
public static Set<String> getKeys(String cacheName,Object... key);


为与注解的互通使用,工具类提供的方法中均使用了 String cacheName 和 Object... key 作为入参。 工具类使用时,入参保持一致就行,不必关心缓存中具体使用的key的形式。 关于注解与该工具类的互通使用,可以参考注解与工具类互通使用示例

String cacheName为缓存名称,对应注解属性cacheNames。

Object... key 为缓存key参数。可传递多个共同生成最终的缓存key.

与注解的交互如下:

若使用注解时未指定注解的key属性(即采用默认的key生成方式), Object... key 传入的值为对应方法的入参; 例如:带有注解的方法入参为 test01(String value,String number) ,调用方式时传入的参数为 "test" 和 2233 ,则调用在使用工具类获取时, Object... key 中需要传入的参数为 "test",2233 两项

若指定了注解key属性,需传入与指定方式所对应的值;例如:带有注解的方法入参为 test01(String value,String number) , 调用方式时传入的参数为 "test" 和 2233 ,指定的key的spEl表达式为 #value+'_'+#number。 此时最终生成的缓存key为 test_2233。若想要通过工具类调用,Object... key 中需要传入 test_2233 一项。

其他场景下(无需与注解对应),只传入缓存key即可。

key的默认生成规则:获取方法的所有入参(对应方法中的 Object... key),各个入参之间使用英文冒号(:)拼接。 例如:方法的所有入参为 "ceshi01",28,"山东" 三项,采用默认生成方式生成的key为 ceshi01:28:山东

注意:若未开启租户功能或者未设置租户,生成的缓存key不拼接租户ID。


# CacheUtil

工具类CacheUtil提供的方法如下:

/**
 * 获取缓存中的数据
 *
 * @param cacheName
 * @param key
 * @param <T>
 * @return
 */
public static <T> T get(String cacheName, String key);

/**
 * 向缓存中存放数据
 *
 * @param cacheName
 * @param value
 * @param key
 */
public static void put(String cacheName, Object value,String key);


/**
 *删除缓存中的数据
 *
 * @param cacheName
 * @param key
 */
public static void evict(String cacheName, String key);

/**
 * 清理对应缓存名称下所有的缓存
 *
 * @param cacheName
 */
public static void clear(String cacheName);

/**
 * 判断缓存key是否存在
 *
 * @param key 索引
 * @return 是否
 */
public static boolean containKey(String cacheName,String  key);

/**
 * 获取过期时间,单位为秒.返回0代表永久有效,-1代表获取失败(key不存在)
 *
 * @param cacheName
 * @param key
 * @return
 */
public static int getExpire(String cacheName, String key);

/**
 * 获取指定前缀的一系列key
 *
 * @param cacheName 缓存名称
 * @param key   缓存key
 * @return
 */
public static Set<String> getKeys(String cacheName,String key);


/**
 * 向缓存中存放数据,设置过期时间
 *
 * @param cacheName 缓存名称
 * @param value 向缓存中存放的数据
 * @param expireSecond 失效时间单位为秒
 * @param key   缓存key参数
 */
public static void putESecond(String cacheName,Object value,int expireSecond,String key);

# 使用示例

# 注解的使用

自定义类

/**
 * 自定义的类
 */
@Service
public class AccountService {
    /**
     * HOSCacheable中的值为之前在配置项中配置过的CacheName的值,缓存的key值会在调用的时候自动生成
     * 缓存端key是根据入参自动生成,其保存的数据为接口的返回值 
     * 调用方法时,根据该生成的key值到缓存中查找,如果找到直接返回缓存中的数据,若没有,将该key对应的数据放入缓存中
     *
     * 方法功能描述:1、打印数据  2、根据入参的testUserName和一个随机数生成返回值
     * 
     * @param testUserName
     * @param value2
     * @return
     */
    @HOSCacheable("cahceTest002")
    public String getAccount(String testUserName, int value2) {
        System.out.println("获取数据中。。1。。。。");
        return testUserName + String.valueOf(new Random().nextInt(100));
    }
}

方法调用

    @Autowired
    AccountService accountService;
    public void testCache() throws Exception {
        System.out.println("第一次执行。。。。。。");
        String result1=accountService.getAccount("UserTestName228",1122);
        System.out.println("结果1:"+result1);

        System.out.println("第二次执行。。。。。。");
        String result2=accountService.getAccount("UserTestName228",1122);
        System.out.println("结果2:"+result2);
    }

调用结果

img.png

如图中展示,第一次调用的时候进入到方法里面,并运算得到结果,第二次调用方式时,未进入方法内部,直接获取到缓存中的值

# 工具类的使用

CacheTenantUtil工具类使用示例CacheUtil工具类使用示例分别如下:

# CacheTenantUtil工具类使用示例

public void testCache() throws Exception {
    String savedData="";

    //初始化存放到缓存中的数据
    List<String> arr=new ArrayList<>();
    arr.add("11");
    arr.add("33");
    Map<String,List> map=new HashMap<>();
    map.put("list",arr);
    
    Object cache1=null;

    //存储缓存数据
    //第一个参数为cacheName(预先设置好的cacheName的值)
    //第二个参数为需要存储的对象
    //其他参数为key值,多个入参共同生成key
    CacheTenantUtil.put("cahceTest002",map,"testName4",1);

    //存储缓存数据,并设置过期时间
    //第一个参数为cacheName(预先设置好的cacheName的值)
    //第二个参数为过期时间
    //第三个参数为需要存储的对象
    //其他参数为key值,多个入参共同生成key
    CacheTenantUtil.putESecond("cahceTest002",arr,600,"testName5");

    //获取数据,第一个参数为cacheName(预先设置好的cacheName的值)
    //其他参数为key值,多个入参共同生成key
    cache1=CacheTenantUtil.get("cahceTest002","testName4",1);
    System.out.println("结果1:"+cache1);

    //删除key下的数据,第一个参数为cacheName(预先设置好的cacheName的值)
    //其他参数为key值,多个入参共同生成key
    CacheTenantUtil.evict("cahceTest002","testName4",1);

    //查询该缓存名称对应的缓存下,所有的缓存key。缓存名称(cacheName)为cahceTest002
    Set<String> keySet=CacheTenantUtil.getKeys("cahceTest002");
    System.out.println("缓存名称cahceTest002中的key:");
    keySet.stream().forEach(key ->{
        System.out.println(key);
    });
}

调用结果:

img.png

# CacheUtil工具类使用示例


String savedData="";

//初始化存放到缓存中的数据
List<String> arr=new ArrayList<>();
arr.add("11");
arr.add("33");
Map<String,List> map=new HashMap<>();
map.put("list",arr);

Object cache1=null;

//存储缓存数据,
//第一个参数为cacheName(预先设置好的cacheName的值)
//第二个参数为需要存储的对象
//第三个参数为缓存key
CacheUtil.put("cahceTest002",map,"customKey_1");
CacheUtil.put("cahceTest002",map,"customKey_3");

//存储缓存数据,并设置过期时间
//第一个参数为cacheName(预先设置好的cacheName的值)
//第二个参数为过期时间
//第三个参数为需要存储的对象
//第四个参数为缓存key
CacheUtil.putESecond("cahceTest002",arr,600,"customKey_2");

//获取数据
//第一个参数为cacheName(预先设置好的cacheName的值)
//第二个参数为缓存key
cache1=CacheUtil.get("cahceTest002","customKey_1");
System.out.println("结果1:"+cache1);

//删除key下的数据
//第一个参数为cacheName(预先设置好的cacheName的值)
//第二个参数为缓存key
CacheUtil.evict("cahceTest002","customKey_1");

//查询该缓存名称对应的缓存下,所有的缓存key。缓存名称(cacheName)为cahceTest002
Set<String> keySet=CacheUtil.getKeys("cahceTest002","");
System.out.println("缓存名称cahceTest002中的key:");
keySet.stream().forEach(key ->{
    System.out.println(key);
});

调用结果:

img.png

# 注解与工具类互通使用示例

  • 注解未指定key属性

使用注解存放缓存

@HOSCacheable("cacheTest001")
public String getAccount004(String testUserName)

使用该工具类获取缓存

CacheTenantUtil.get("cacheTest001",testUserName);

使用该工具类清理缓存

CacheTenantUtil.evict("cacheTest001",testUserName);
  • 注解指定key属性

使用注解存放缓存

@HOSCacheable(cacheNames = "cacheTest001",key ="#testUserName+'-01'")
public String getAccount004(String testUserName)

此时,由于注解中key为使用者指定,因此在使用工具类操作缓存时,需要使用者自己完成拼接

若,testUserName值为 testUser,则根据示例中key属性中指定的方式,生成的key为 testUser-01,因此

使用该工具类获取缓存

CacheTenantUtil.get("cacheTest001","testUser-01");

使用该工具类清理缓存

CacheTenantUtil.evict("cacheTest001","testUser-01");