SpringBoot缓存之整合Ehcache

2022-11-14  本文已影响0人  上善若泪

1 Ehcache

点此了解Ehcache原理

1.1 pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

<!-- ehcache坐标 -->
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
</dependency>

1.2 ehcache.xml

1.2.1 示例

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <diskStore path="java.io.tmpdir"/>

    <!--defaultCache:echcache 的默认缓存策略 -->
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxElementsOnDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </defaultCache>
    <!-- 自定义缓存策略 -->
    <cache name="users"
           maxElementsInMemory="10000"
           eternal="false"
           timeToIdleSeconds="120"
           timeToLiveSeconds="120"
           maxElementsOnDisk="10000000"
           diskExpiryThreadIntervalSeconds="120"
           memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </cache>

</ehcache>

1.2.2 参数说明

文件中参数说明:

1.3 修改启动类

添加注解@EnableCaching,说明开启注解缓存

@SpringBootApplication
@EnableCaching
@MapperScan("cn.jzh.mapper")
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}    

1.4 缓存数据

1.4.1 @Cacheable

@Cacheable注解在方法上,表示该方法的返回结果是可以缓存的。也就是说,该方法的返回结果会放在缓存中,以便于以后使用相同的参数调用该方法时,会返回缓存中的值,而不会实际执行该方法。

注意:这里强调了一点:参数相同
这一点应该是很容易理解的,因为缓存不关心方法的执行逻辑,它能确定的是:对于同一个方法,如果参数相同,那么返回结果也是相同的。但是如果参数不同,缓存只能假设结果是不同的,所以对于同一个方法,程序运行过程中,使用了多少种参数组合调用过该方法,理论上就会生成多少个缓存的 key(当然,这些组合的参数指的是与生成 key 相关的)

1.4.1.1 常见属性

@Cacheable这个注解常用的几个属性:

1.4.1.2 key & keyGenerator

需要提前注意指定key,需要以#打头
官方说 keykeyGenerator 参数是互斥的,同时指定两个会导致异常

显示指定key,实现动态拼接

@Cacheable(value = "users",key = "#root.methodName+'[' + #id +']'")
    public Object queryUserList(Integer id){
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("id",1);
        List<User> users = userMapper.selectList(wrapper);
        return JSON.toJSON(users);
    }

自定义keyGenerator

import org.springframework.cache.interceptor.KeyGenerator;

import java.lang.reflect.Method;
import java.util.Arrays;

@Configuration
public class MyCacheConfig{
    @Bean("myKeyGenerator ")
    public KeyGenerator keyGenerator(){
        return new KeyGenerator(){
            @Override
            public Object generate(Object target, Method method, Object... params) {
                return method.getName()+"["+ Arrays.asList(params).toArray()+"]";
            }
        };
    }
}

自定义keyGenerator使用例子

@Cacheable(value = "users",keyGenerator = "myKeyGenerator")
    public Object queryUserList(Integer id){
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("id",1);
        List<User> users = userMapper.selectList(wrapper);
        return JSON.toJSON(users);
    }

1.4.1.3 使用spEL编写key

前面说过,缓存的 key 支持使用 spEL 表达式去编写,下面总结一下使用 spEL 去编写 key 可以用的一些元数据

名字 位置 描述 示例
methodName root object 当前被调用方法名 #root.methodName
method root object 当前被调用方法 #root.method.name
target root object 当前被调用目标对象 #root.target
targetClass root object 当前被调用目标对象类 #root.targetClass
args root object 当前被调用方法参数列表 #root.args[0]
caches root object 当前方法调用使用的缓存列表 ,如@Cacheable({"menu", "menuById"}),则有两个cache #root.caches[0].name
argument name evaluation context 方法参数名字,可以直接使用 #参数名,也可以使用#p0或者#a0形式,0代表参数索引 #a0
result evaluation context 方法执行后的返回值(仅当方法执行后的判断有效) #result

1.4.1.4 cacheManager & cacheResolver

CacheManager,缓存管理器是用来管理(检索)一类缓存的。通常来讲,缓存管理器是与缓存组件类型相关联的。我们知道,spring 缓存抽象的目的是为使用不同缓存组件类型提供统一的访问接口,以向开发者屏蔽各种缓存组件的差异性。那么 CacheManager 就是承担了这种屏蔽的功能。spring 为其支持的每一种缓存的组件类型提供了一个默认的 manager,如:RedisCacheManager 管理 redis 相关的缓存的检索、EhCacheManager 管理 ehCache 相关的缓等。

CacheResolver,缓存解析器是用来管理缓存管理器的,CacheResolver 保持一个 cacheManager 的引用,并通过它来检索缓存。CacheResolver 与 CacheManager 的关系有点类似于 KeyGenerator 跟 key。spring 默认提供了一个 SimpleCacheResolver,开发者可以自定义并通过 @Bean 来注入自定义的解析器,以实现更灵活的检索。

大多数情况下,我们的系统只会配置一种缓存,所以我们并不需要显式指定 cacheManager 或者 cacheResolver。但是 spring 允许我们的系统同时配置多种缓存组件,这种情况下,我们需要指定。指定的方式是使用 @CacheablecacheManager 或者 cacheResolver 参数。

注意:按照官方文档,cacheManagercacheResolver 是互斥参数,同时指定两个可能会导致异常

1.4.2 @CachePut

当需要在不影响方法执行的情况下更新缓存时,可以使用@CachePut,也就是说,被 @CachePut 注解的缓存方法总是会执行,而且会尝试将结果放入缓存(当然,是否真的会缓存还跟一些注解参数有关,比如:unless 参数)。@CachePut@Cacheable 有相同的参数属性(但是没有 sync 属性)。@CachePut 更加适合于缓存填充,而不是方法执行流的优化。

由于与 @Cacheable 的属性基本相同,所以不再重复示例。这里重点说明一下它们的区别:

所以 @Cacheable 适用于查询数据的方法,@CachePut 适用于更新数据的方法。

1.5 清除缓存@CacheEvict

1.5.1 常见属性

除了填充缓存,spring cache也支持使用 @CacheEvict 来删除缓存。@CacheEvict就是一个触发器,在每次调用被它注解的方法时,就会触发删除它指定的缓存的动作。跟 @Cacheable@CachePut 一样,@CacheEvict 也要求指定一个或多个缓存,也指定自定义一的缓存解析器和 key 生成器,也支持指定条件(condition 参数)。

@CacheEvict是用来清除缓存的,有以下属性:

1.5.2 beforeInvocation

beforeInvocation@CacheEvict 中特有的一个属性,意为是否在执行对应方法之前删除缓存,默认 false(即执行方法之后再删除缓存)。
首先思考一个问题,在什么情况下我们需要主动去删除缓存呢?一般来讲都是在删除数据的时候,需要主动去删除缓存。那么就存在一个问题,程序执行时顺序的,那我们到底是应该先删除缓存,再调用方法去数据库中删除;还是先从数据库中删除,完了之后再去删除对应的缓存呢?

在正常情况下,这两种方式差别并不大,毕竟程序执行都是毫秒级的,顺序执行没有什么时间跨度。但是,现实环境复杂,缓存访问和 db 访问都可能会出现异常,这种情况下就有区别了:

至于该如何取舍,spring cache 通过 beforeInvocation 给开发者提供选择

上一篇下一篇

猜你喜欢

热点阅读