目录
简介
Spring 的缓存主要有如下几个注解
红色标注的注解为最常用的注解,必须熟练掌握
@Cacheable/@CachePut/@CacheEvict 主要的参数
SpEL 提供的运算符
实现步骤
补充
注意
整合 EHCACHE
简介
Spring 框架已经具备了缓存机制,虽然我们可以使用 Redis 等作为缓存处理机制,不过对于非常零碎的数据交互其实使用 Spring 的缓存机制反而显得简单;而且我们可以将 Spring 的缓存机制融合 Redis 和 关系型数据库建立一个完备的数据处理方案,相当于实现了 3 级缓存,将非常有助于超大型系统的数据交互压力
Spring 的缓存主要有如下几个注解
红色标注的注解为最常用的注解,必须熟练掌握
@Cacheable/@CachePut/@CacheEvict 主要的参数
SpEL上下文数据
Spring 为我们提供了一个 root 对象可以用来生成 key,通过该 root 对象我们可以获取到以下信息
SpEL 提供的运算符
注意
(1) 当我们要使用 root 对象的属性作为 key 时我们也可以将 “#root” 省略,因为 Spring 默认使用的就是 root 对象的属性; 如:
@Cacheable(key="targetClass + methodName +#p0")
(2) 使用方法参数时我们可以直接使用“#参数名”或者“#p参数 index ”, 如:
@Cacheable(value="users", key="#id")@Cacheable(value="users", key="#p0")
实现步骤
1 因为我们使用了 SpringBoot 框架,而 SpringBoot 框架已经包含了缓存所依赖的包,所以直接使用上面的注解即可,不用添加如下依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
2. 在 SpringBoot 项目的启动类上添加 @EnableCaching 注解,表示打开缓存驱动
@SpringBootApplication
@EnableCaching
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
3. 新建 CacheService.class,添加如下 3 个方法
@Service
public class CacheService {
// 如果缓存中没有要查询的值, 将执行方法中的代码, 并将返回值更新到缓存
// 如果缓存中有要查询的值, 将不会执行方法中的代码, 而是直接从缓存返回值
@Cacheable(cacheNames = {"myCache"}, key = "#root.targetClass")
public String get() {
System.out.println(new Date() + " --> 没有从缓存取值");
return“ Ramos”;
}
// 该注解将会向缓存中 添加/更新 新的值
// 与 @Cacheable 不同的是, 如下方法中的代码都会执行
@CachePut(cacheNames = {"myCache"}, key = "#root.targetClass")
public String put(String value) {
System.out.println(new Date() + " 添加了 value --> " + value);
return value;
}
// 该注解将会把缓存中的值删除掉
// 与 @Cacheable 不同的是, 如下方法中的代码都会执行
@CacheEvict(cacheNames = {"myCache"}, key = "#root.targetClass")
public void delete(String key) {
System.out.println(new Date() + " 删除了 value ");
}
}
4. 在 application.properties 配置文件中添加如下配置
# 指定使用缓存管理器
spring.cache.type=simple
# 定义全局缓存名
spring.cache.cache-names=xxx,xxx...
# 指定缓存管理器的配置文件(非必须, 对于某些有配置文件的缓存插件才需要配置)
#spring.cache.config=classpath:xxx.xml
如果没有添加上面的注解,会报错:Cannot find cache named xxx; cache.type 可选值如下:
GENERIC,
JCACHE,
EHCACHE,
HAZELCAST,
INFINISPAN,
COUCHBASE,
REDIS,
CAFFEINE,
SIMPLE,
NONE;
补充
1. @Cacheable 注解,主要有如下 2 个作用:
(1) 当缓存中没有要的值的时候,会执行该注解下的方法,并将该方法的返回值保存到缓存
(2) 当缓存中有要的值的时候,不会执行注解下的方法,将直接返回缓存中的值
2. @Cacheput 注解:主要是更新缓存中的值,不返回值,有则覆盖,没有则添加
3. @CacheEvict 注解:将缓存中的值删除,注意
(1) 该注解的 allEntries 属性,当设置为 true 的时候,将会忽略所有的 key,清除所有的缓存
(2) 该注解 beforeInvocation 属性, 清除操作默认是在对应方法成功执行之后触发的,即方法如果因为抛出异常而未能成功返回时也不会触发清除操作;使用 beforeInvocation 可以改变触发清除操作的时间,当指定该属性值为 true 时,Spring 会在调用该方法之前清除缓存中的指定元素
4. 以上注解中的 cacheNames 属性可以理解为 数据库的库,而 key 属性可以理解为 数据库的表;
如果一个方法上添加的是@Cacheable (cacheNames="a", key="1"); 而另一个方法上添加的@CacheEvict(cacheNames="a", key="2"); 他们的 cacheNames(相当于数据库)相同;但是 key(相当于表) 不同,当执行了清理缓存的方法后,key=“1” 的缓存依然还在;cacheNames 属性和 value 属性等效,参数都是 String 数组,不过为了避免误解(该 value 不是往缓存里存的那个 value,千万不要混淆!!!!!!!!!!),建议还是使用 cacheNames 属性
5. @CacheConfig 注解的使用
从实现步骤 3 中可以看到,每个方法上的注解都有相同的属性,虽然注解不一样,但是 cacheNames 属性和 key 属性是一样的,那么如果有很多个方法,且他们的参数都一样的话,代码看起来就会显得很冗余,这个时候就可以使用 @CacheConfig 注解来解决这个问题;经过修改后的 CacheService.class 代码如下
@Service
@CacheConfig(cacheNames={"myCache"}, keyGenerator="cacheKeyGenerator")
public class CacheService {
@Cacheable
public String save() {
System.out.println(new Date() + " --> 没有从缓存取值");
return "Ramos";
}
}
注意
@CacheConfig 注解,该注解的 cacheNames 属性自然是为所有的缓存注解标明缓存名称(可理解为数据库名),keyGenerator 属性(非必需)表示所有缓存的 key 的生成策略,需要为其注入一个 Bean,且该 Bean 要实现 KeyGenerator 接口,并要重写 generate 方法;如下是我们自定义的 CacheKeyGenerator 类,注意和 keyGenerator 属性值对比
@Component
// 自定义缓存 key 的生成策略
public class CacheKeyGenerator implements KeyGenerator {
// 将目标类的类名作为 key 值
@Override
public Object generate(Object target, Method method, Object... params) {
return target.getClass().getName();
}
}
6. 组合@Caching
有时候我们可能组合多个Cache注解使用,此时就需要@Caching组合多个注解标签了,如下
@Caching(cacheable = {
@Cacheable(value = "emp",key = "#p0"),
...
},
put = {
@CachePut(value = "emp",key = "#p0"),
...
},evict = {
@CacheEvict(value = "emp",key = "#p0"),
....
})
public User save(User user) {
....
}
整合 EHCACHE
Ehcache 是一种广泛使用的开源 Java 分布式缓存,主要面向通用缓存,Java EE 和轻量级容器;它具有内存和磁盘存储,缓存加载器,缓存扩展,缓存异常处理程序,一个 gzip 缓存 servlet 过滤器,支持 REST和 SOAP API 等特点;其实对于一般的使用来说,上面介绍的缓存用法已经足够,Ehcache 最大的优点是可以将缓存中的内容写入到本地磁盘中,并可配置缓存过期时间
1. 首先需要添加如下 3 个依赖,缺一不可
<!-- ehcache 缓存 -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
<!--开启缓存支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
</dependency>
2. 在 src/main/resource 目录下创建 ehcache.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<!-- 磁盘存储:
将缓存中暂时不使用的对象,转移到硬盘,类似于Windows系统的虚拟内存
path:指定在硬盘上存储对象的路径 path可以配置的目录有
user.home(用户的家目录)
user.dir(用户当前的工作目录)
java.io.tmpdir(默认的临时目录)
ehcache.disk.store.dir(ehcache的配置目录)
绝对路径(如:d:\\ehcache) 查看路径方法:String tmpDir = System.getProperty("java.io.tmpdir");
-->
<!-- <diskStore path="java.io.tmpdir" /> -->
<diskStore path="c:/cache" />
<!-- defaultCache: 默认的缓存配置信息,如果不加特殊说明,则所有对象按照此配置项处理
maxElementsInMemory: 设置了缓存的上限,最多存储多少个记录对象
eternal: 代表对象是否永不过期 (指定true则下面两项配置需为0无限期)
timeToIdleSeconds: 最大的发呆时间 /秒
timeToLiveSeconds: 最大的存活时间 /秒
overflowToDisk: 是否允许对象被写入到磁盘
说明: 下列配置自缓存建立起10秒有效
在有效的10内,如果连续10未访问缓存,则缓存失效
就算有访问,也只会存活10秒
-->
<defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="10" timeToLiveSeconds="10" overflowToDisk="false"/>
<cache name="myCache" maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="10" timeToLiveSeconds="10" overflowToDisk="true"/>
</ehcache>
3. 在 application.properties 配置文件中添加如下配置
# 指定缓存配置文件(也可以使用 spring.cache.ecache.config=xxx)
spring.cache.jcache.config = classpath:ehcache.xml