§ 本文目标
通过本章节练习,掌握基于Spring Cache + Spring boot的入门级开发
§ 练习场景
额度管理信息查询提供缓存支持
前提:
- 基于CRUD单表增删该查章节,完成该练习
- Redis环境,基于Redis提供缓存功能;参考Redis部署章节下的Redis部署
- 工具Postman PLSQL RedisDesktopManager
§ 操作步骤
§ 基础知识准备
Spring cache主要提供了以下几个Annotation:
| 注解 | 适用的方法类型 | 作用 |
|---|---|---|
| @Cacheable | 读数据 | 方法被调用时,先从缓存中读取数据,如果缓存没有找到数据,再调用方法获取数据,然后把数据添加到缓存中 |
| @CachePut | 写数据:如新增/修改数据 | 调用方法时会自动把相应的数据放入缓存 |
| @CacheEvict | 删除数据 | 调用方法时会从缓存中移除相应的数据 |
属性说明:
- alue、cacheNames:两个等同的参数(cacheNames为Spring 4新增,作为value的别名),用于指定缓存存储的集合名。由于Spring 4中新增了@CacheConfig,因此在Spring 3中原本必须有的value属性,也成为非必需项了
- key:缓存对象存储在Map集合中的key值,非必需,缺省按照函数的所有参数组合作为key值,若自己配置需使用SpEL表达式,比如:@Cacheable(key = "#p0"):使用函数第一个参数作为缓存的key值,更多关于SpEL表达式的详细内容可参考官方文档
- condition:缓存对象的条件,非必需,也需使用SpEL表达式,只有满足表达式条件的内容才会被缓存,比如:@Cacheable(key = "#p0", condition = "#p0.length() < 3"),表示只有当第一个参数的长度小于3的时候才会被缓存。 unless:另外一个缓存条件参数,非必需,需使用SpEL表达式。它不同于condition参数的地方在于它的判断时机,该条件是在函数被调用之后才做判断的,所以它可以通过对result进行判断
- keyGenerator:用于指定key生成器,非必需。若需要指定一个自定义的key生成器,我们需要去实现org.springframework.cache.interceptor.KeyGenerator接口,并使用该参数来指定。需要注意的是:该参数与key是互斥的
- cacheManager:用于指定使用哪个缓存管理器,非必需。只有当有多个时才需要使用
- org.springframework.cache.interceptor.CacheResolver接口来实现自己的缓存解析器,并用该参数指定
§ 模板配置
引入缓存依赖包(微服务模板已默认引入)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
2
3
4
5
6
7
8
9
10
11
12
修改Apollo配置中心,微服务的redis配置,修改host为redis主机地址
spring:
# 如不使用redis,请将以下配置删除
redis:
database: 0 # Redis默认情况下有16个分片,这里配置具体使用的分片,默认是0
host: localhost # Redis的地址
port: 6379 # Redis的端口
password: 123456 # Redis的密码
timeout: 60000 # 连接超时时间(毫秒)
# 使用lettce最为redis客户端
lettuce:
shutdown-timeout: 60000ms # 关闭超时时间
pool:
max-active: 20 # 连接池最大连接数(使用负值表示没有限制) 默认 8
max-wait: 20000ms # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
min-idle: 1 # 连接池中的最小空闲连接 默认 0
max-idle: 20 # 连接池中的最大空闲连接 默认 8
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
§ Service开发
实现查询、修改、删除的缓存实时更新功能
@Service
public class LimitInfoService extends CommonService {
@Autowired
private LimitInfoMapper limitInfoMapper;
@Override
protected CommonMapper<?> getMapper() {
return limitInfoMapper;
}
/**
* 额度信息查询.
* <p>
* 第一次查询,从数据库获取,如果返回结果为空,则不缓存
* 第二次查询,从缓存获取。
*
* @param limitId
* @return
*/
@Cacheable(value = "LimitInfo", key = "#limitId", unless= "#result == null")
public LimitInfo queryWithCache(String limitId) {
return limitInfoMapper.selectByPrimaryKey(limitId);
}
/**
* 缓存修改.
* <p>
* 修改对象后,更新缓存对象.
*
* @param limitInfo
* @return
*/
@CachePut(value = "LimitInfo", key = "#limitInfo.limitId", unless= "#result == null")
public LimitInfo updateWithCache(LimitInfo limitInfo) {
limitInfoMapper.updateByPrimaryKey(limitInfo);
return limitInfo;
}
/**
* 缓存删除.
* <p>
* 删除数据库记录后,同时删除缓存信息.
*
* @param limitInfo
* @return
*/
@CacheEvict(value = "LimitInfo", key = "#limitId")
public void deleteWithCache(String limitId) {
limitInfoMapper.deleteByPrimaryKey(limitId);
}
}
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
§ Resource开发
提供API接口
@RestController
@RequestMapping("/api/limitinfo")
public class LimitInfoResource extends CommonResource<LimitInfo, String> {
@Autowired
private LimitInfoService limitInfoService;
@Override
protected CommonService getCommonService() {
// TODO 自动生成的方法存根
return limitInfoService;
}
@GetMapping(value = "/query")
public LimitInfo query(@RequestParam(value = "limitId") String limitId) {
return limitInfoService.selectByPrimaryKey(limitId);
}
/**
* 基于缓存实现查询.
*
* @param limitId
* @return
*/
@GetMapping(value = "/querycache")
public LimitInfo queryWithCache(@RequestParam(value = "limitId") String limitId) {
return limitInfoService.queryWithCache(limitId);
}
/**
* 修改同时更新缓存.
*
* @param limitId
* @return
*/
@PostMapping(value = "/updatecache")
public LimitInfo updateWithCache(@RequestBody LimitInfo limitInfo) {
return limitInfoService.updateWithCache(limitInfo);
}
/**
* 删除同时清理缓存.
*
* @param limitId
* @return
*/
@GetMapping(value = "/deletecache")
public void deleteWithCache(@RequestParam(value = "limitId") String limitId) {
limitInfoService.deleteWithCache(limitId);
}
}
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
§ 测试
§ 查询
第一次查询,从数据库查询数据后,设置信息到缓存数据库,耗时150ms+

缓存数据信息:

第二次查询,响应耗时90ms左右,降低了2/5,查询性能有了很大的提升

§ 修改limitDesc内容为airabl_update
http://localhost:6001/api/limitinfo/updatecache
{
"limitId": "00000",
"limitCode": "101010",
"limitAmt": "2222222",
"limitDesc": "airabl_update",
"limitUserId": "air"
}
2
3
4
5
6
7


缓存数据库信息,成功更新为最新记录

§ 删除
http://localhost:6001/api/limitinfo/deletecache?limitId=00000

redis缓存数据库结果查询,信息已经被清理,数据库记录被删除

§ 进阶
§ Spring Cache 基本接口
Spring cache只是对缓存的抽象,并不是缓存的一个具体实现。不同的具体缓存实现方案可能会有各自不同的特性。Spring cache作为缓存的抽象,它只能抽象出共同的属性/功能,对于无法统一的那部分属性/功能它就无能为力了,这部分差异化的属性/功能应该由具体的缓存实现者去配置。
Spring框架提供了基础的两个接口类,用户第三缓存应用进行扩展
缓存管理接口
package org.springframework.cache;
import java.util.Collection;
/**
* Spring's central cache manager SPI.
* Allows for retrieving named {@link Cache} regions.
*
* @author Costin Leau
* @since 3.1
*/
public interface CacheManager {
/**
* Return the cache associated with the given name.
* @param name the cache identifier (must not be {@code null})
* @return the associated cache, or {@code null} if none found
*/
Cache getCache(String name);
/**
* Return a collection of the cache names known by this manager.
* @return the names of all caches known by the cache manager
*/
Collection`<String>` getCacheNames();
}
package org.springframework.cache;
import java.util.concurrent.Callable;
/**
* Interface that defines common cache operations.
*
* <b>Note:</b> Due to the generic use of caching, it is recommended that
* implementations allow storage of <tt>null</tt> values (for example to
* cache methods that return {@code null}).
*
* @author Costin Leau
* @author Juergen Hoeller
* @author Stephane Nicoll
* @since 3.1
*/
public interface Cache {
...
}
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
§ 常用实现类
常用的缓存,已经提供了实现类:

如果系统未配置任何第三方缓存信息,系统默认通过ConcurrentHashMap、ConcurrentMapCache、SimpleCacheManager,由JVM提供缓存支持
§ 注意事项
只有在Bean的入口方法,注解才能生效(Bean内部的方法调用,注解不会生效)
← 多线程开发 事务开发-分布式事务 →