SpringCache 注解增删改

我们在 起步程序 中简单实用了 @Cacheable 注解
接下来介绍几个功能性注解。

# 功能性注解

# 注解属性与 key 生成

先说明一下 SpringCache 在注解里面的一些属性名:

属性名 作用
cacheNames / value 缓存的名字,可以理解为 key 的前缀(“姓”)
key 缓存时 key 的主体(“名”)
keyGenerator 缓存生成策略,和 key 二选一,可自定义工具自动生成对应格式的 key
cacheManager / cacheResolver 缓存管理器,两者二选一
condition 指定缓存的条件,如 condition = "#star>100" 就是当参数中的 star 超过 100100 了才进行缓存
unless 不进行缓存的条件,和上面使用方法一样但是是不进行缓存
sync 是否使用异步缓存,默认为 false

其中对于 key 的生成,SpringCache 给出了一个 root 对象可以获得全局的一些信息

属性名 表示信息 示例
methodName 当前方法名 #root.methodName
method 当前方法 #root.method.name
target 当前被调用对象 #root.target
targetClass 当前被调用对象的类 #root.targetClass
args 当前方法组成的数组 #root.args[0]
caches 当前方法使用的 Cache #root.caches

比如下面代码

...
    @Cacheable(value = "website", key = "#root.methodName+'_'+#name")
    public Website get (String name) {
        return new Website(name, 0);
    }
...
1
2
3
4
5
6

调用 get(snopzyz) 生成的缓存就叫做 website::get_snopzyz

# @Cacheable

注解内的属性这里都有。
一般标注在方法上,用作缓存结果,key 通常为参数中的信息。
在方法运行开始之前用配置的注解属性生成 key 的 String 值,并判断该 key 是否存在于缓存库中

  • 如果不存在:执行方法体,并将结果添加进缓存中
  • 如果存在:绕过方法体,直接通过缓存库中的 value 进行返回

示例:根据网站 name 获取网站信息 (name,star)

@Cacheable(key = "#name")
public Website get (String name) {
    return new Website(name, 0);
}
1
2
3
4

# @CachePut

注解属性没有 sync ,别的都有。
一般标注在方法上,不管有没有对应 key 的缓存,本方法体全部执行,然后将返回值加入缓存,一般用作缓存的修改

示例:根据网站 name,做网站关注给关注量 star 加一,并将结果返回
(这里启用两个是因为在同一个类做内部方法互相调用的时候,会导致 proxy 失效那么 aop 切面就会失效,所以将要被调用的方法切到另一个类中

@Service
public class WebsiteService {

    @Autowired
    private WebsiteCacheableService cacheableService;

    public Website get (String name) {
        return cacheableService.get(name);
    }

    /**
     * 根据 name 给网站添加 star 收藏数
     * @param name 网站名
     * @return 添加 star 后的网站
     */
    @CachePut(value = "website", key = "#name")
    public Website star (String name) {
        Website website = cacheableService.get(name);
        website.setStar(website.getStar() + 1);
        return website;
    }
}
@Service
class WebsiteCacheableService {

    /**
     * 根据网站 name 查询网站信息,封装为 Website 类的 json 返回
     * @param name 网站名
     * @return 网站信息的 Website json 格式
     */
    @Cacheable(value = "website", key = "#name")
    public Website get (String name) {
        return new Website(name, 0);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# @CacheEvict

用作缓存的移除,表示从缓存库中删除对应的 key

@CacheEvict(value = "website", key = "#name")
public Boolean delete (String name) {
    return true;
}
1
2
3
4

# @Caching

用于组合多个 Cache 注解

    @Caching(
        cacheable = {
            @Cacheable(value = "website1", key = "#name"),
            @Cacheable(value = "website2", key = "#root.methodName")
        },
        put = {
            @CachePut(value = "website", key = "#name")
        },
        evict = {
            @CacheEvict(value = "website-demo", key = "#root.methodName")
        }
    )
    public void groupOpe (String name) {
        ...
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

这样可以完成缓存操作的嵌套

# @CacheConfig

放在类名上,可以通过 cacheNames 注解属性为本类所有的添加了缓存的方法设置公共的 value

@Service
@CacheConfig(cacheNames = "website")
class WebsiteCacheableService {

    /**
     * 根据网站 name 查询网站信息,封装为 Website 类的 json 返回
     * @param name 网站名
     * @return 网站信息的 Website json 格式
     */
    @Cacheable(key = "#name")
    public Website get (String name) {
        return new Website(name, 0);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

这个 get 实际上的 @Cacheable 相当于 @Cacheable(value = "website", key = "#name")

# 实际使用:网站的创建、点赞与删除

# 代码部分

这里不加任何复杂的应用(如 MySQL 持久化),纯用 SpringCache 集成 Redis 在缓存中完成操作。

将我们上面演示注解用的方法拼成如下的 Service 层代码

// com.snopzyz.service. WebsiteService.java

@Service
@CacheConfig(cacheNames = "website")
public class WebsiteService {

    @Autowired
    private WebsiteCacheableService cacheableService;

    public Website get (String name) {
        return cacheableService.get(name);
    }

    /**
     * 根据 name 给网站添加 star 收藏数
     * @param name 网站名
     * @return 添加 star 后的网站
     */
    @CachePut(key = "#name")
    public Website star (String name) {
        Website website = cacheableService.get(name);
        website.setStar(website.getStar() + 1);
        return website;
    }

    /**
     * 根据 name 删除网站
     * @param name 网站名
     * @return 删除后的结果,都为 true
     */
    @CacheEvict(key = "#name")
    public Boolean delete (String name) {
        return true;
    }

}

@Service
@CacheConfig(cacheNames = "website")
class WebsiteCacheableService {

    /**
     * 根据网站 name 查询网站信息,封装为 Website 类的 json 返回
     * @param name 网站名
     * @return 网站信息的 Website json 格式
     */
    @Cacheable(key = "#name")
    public Website get (String name) {
        return new Website(name, 0);
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

然后修改一下 Controller ,直接调用自动装配的 service

// com.snopzyz.controller. WebsiteController.java

@RestController
@RequestMapping("/website")
public class WebsiteController {

    @Autowired
    private WebsiteService websiteService;

    @GetMapping("/{name}")
    public Website get (@PathVariable String name) {
        System.out.println("WebsiteController 执行 get(" + name + ") 方法");
        return websiteService.get(name);
    }

    @PutMapping
    public Website star (@RequestBody Website website) {
        String name = website.getName();
        System.out.println("WebsiteController 执行 star(" + name + ") 方法");
        return websiteService.star(name);
    }

    @DeleteMapping("/{name}")
    public Boolean delete (@PathVariable String name) {
        System.out.println("WebsiteController 执行 delete(" + name + ") 方法");
        return websiteService.delete(name);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 测试环节

这里就是用了 RestFul 开发风格的请求格式,先确定一下 Redis 库中是否什么数据都没有。
20231103092848

  1. GET请求 http://localhost:8080/website/snopzyz
    1. 控制台:
    2. 响应体:
    3. 缓存库:
  2. POST请求 http://localhost:8080/website/ 并夹带 json 请求体 {"name": "snopzyz"}
    1. 控制台:
    2. 响应体:
  3. DELETE请求 http://localhost:8080/website/snopzyz
    1. 控制台:
    2. 响应体:
      true
    3. 缓存库:

      数据消失

测试符合逻辑与预期,成功

Last Updated: 11/3/2023, 11:09:36 AM