Skip to content

Commit

Permalink
2.4.0发布
Browse files Browse the repository at this point in the history
  • Loading branch information
yl-yue committed Jun 6, 2021
1 parent c684a50 commit 98989bc
Show file tree
Hide file tree
Showing 6 changed files with 257 additions and 54 deletions.
2 changes: 1 addition & 1 deletion docs/_sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@

* [介绍](data/redis/介绍.md "yue-library-data-redis介绍")
* [快速开始](data/redis/快速开始.md "yue-library-data-redis快速开始")
* [分布式锁](data/redis/分布式锁.md "yue-library-data-redis分布式锁")
* [分布式锁与接口幂等性](data/redis/分布式锁与接口幂等性.md "yue-library-data-redis分布式锁与接口幂等性")
* [分布式缓存](data/redis/分布式缓存.md "yue-library-data-redis分布式缓存")

* yue-library-data-es
Expand Down
28 changes: 26 additions & 2 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,34 @@

[👉点击查看pom.xml依赖](https://gitee.com/yl-yue/yue-library/blob/master/pom.xml)

## 2.4.1【规划中】
## 2.4.0【即将发布】
## 2.4.2【规划中】
## 2.4.1【即将发布】
## 2.4.0【2021-06-06】
- 主要变更:使用SpringBoot2.4新的配置文件机制,提供默认的优化配置实现。
- 主要新特性:使用注解`@ApiIdempotent`可优雅的实现接口幂等性

### 新特性
- 【base】新增`@CarDrivingLicence``@CarVin``@CreditCode``@ZipCode`四个校验注解
- 【base】迁移actuator配置至auth模块,添加actuator配置安全
- 【base】actuator端点默认使用32222端口进行访问,与API服务端口进行区分,保持良好的安全忧患意识
- 【base】网络代理,额外不代理地址默认添加所有内网网段
- 【jdbc】完善逻辑删除,delete_time条件追加时判断sql中是否存在delete_time否则不再追加
- 【redis】新增API接口幂等性优雅实现,使用`@ApiIdempotent`注解标注接口需要进行幂等性校验
- 【test】新增模块分离测试
- 【docs】新增安全规约
- 【docs】优化异步线程池示例与完善文档
- 【docs】完善逻辑删除文档
- 【docs】添加分布式缓存示例与文档
- 【docs】完善分布式锁与接口幂等性文档
- 【docs】完善POJO与Lombok的使用说明
- 【docs】添加类型转换器Bean别名规范
- 【docs】完善JavaBean参数解析器文档,提示IPO中有无参构造时,解析List<String>类型需传标准是数组字符串
- 【other】删除部分早已标记为失效的方法

### Bug修复
- 【web】解决SpringBoot2.4版本新出现的跨域问题 [#I3OV7B](https://gitee.com/yl-yue/yue-library/issues/I3OV7B)
- 【web】修复异步线程装饰器在开启ServletAsyncContext时,接口响应被无故追加404异常 [#I3HTAW](https://gitee.com/yl-yue/yue-library/issues/I3HTAW)

### Maven仓库实际发布版本号
`j8.2.4.0``j11.2.4.0`

Expand Down
Binary file added docs/data/redis/redis_files/1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
151 changes: 124 additions & 27 deletions docs/data/redis/分布式缓存.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
# 分布式缓存
Spring为我们提供了三大注解`@Cacheable``@CachePut``@CacheEvict`可在大部分场景下优雅实现分布式缓存。
什么是分布式缓存?在实际开发场景中,往往单机应用无法满足当前的需求,需要对项目进行分布式部署,由此每个项目中的缓存都是属于自己独立服务的,并不能共享,其次当某个服务更新了缓存,其他服务并不知道,当用户请求到其他服务时,获取到的往往还是旧的数据。
这时就需要将缓存的数据放在一个统一的地方进行管理,如:redis

## 注解介绍
注解介绍中所包含的代码片段位于[yue-library-test](https://gitee.com/yl-yue/yue-library/tree/master/yue-library-samples/yue-library-test)模块中
Spring为我们提供了三大注解`@Cacheable``@CachePut``@CacheEvict`可在绝大部分场景下优雅实现分布式缓存。

### @EnableCaching
### @EnableCaching(启用缓存)
一般注解在启动类或配置类上,表示启用缓存,使`@Cacheable``@CachePut``@CacheEvict`等注解生效。

### @Cacheable(添加/获取缓存)
|属性名 |作用 |
|-- |-- |
|cacheNames/value |指定缓存的名字,缓存使用CacheManager管理多个缓存Cache,这些Cache就是根据该属性进行区分。对缓存的真正增删改查操作在Cache中定义,每个缓存Cache都有自己唯一的名字。 |
|key |缓存数据时的key的值,默认是使用方法所有入参的值,可以使用SpEL表达式表示key的值。 |
|keyGenerator |缓存的生成策略(键生成器),和key二选一,作用是生成键值key,keyGenerator可自定义。 |
|cacheManager |指定缓存管理器(例如ConcurrentHashMap、Redis等) |
|cacheResolver |和cacheManager作用一样,使用时二选一 |
|cacheNames/value |缓存名(必须指定至少一个值),即缓存命名空间名称,用于确定缓存目标。 |
|key |缓存key,缺省为按照方法的所有入参进行组合,可以使用SpEL表达式,实际存储key时默认以`cacheNames/value`作为前缀。 |
|keyGenerator |指定key的生成器生成键值key(与`key`二选一),非必需。 |
|cacheManager |指定缓存管理器(例如ConcurrentHashMap、Redis等),非必需。 |
|cacheResolver |和cacheManager作用一样,使用时二选一,非必需。 |
|condition |指定缓存的条件(对参数判断,满足什么条件时才缓存),可用SpEL表达式,例如:方法入参为对象user则表达式可以写为`condition = "#user.age>18"`,表示当入参对象user的属性age大于18才进行缓存。|
|unless |否定缓存的条件(对结果判断,满足什么条件时不缓存),即满足unless指定的条件时,对调用方法获取的结果不进行缓存,例如:`unless = "result==null"`,表示如果结果为null时不缓存。 |
|sync |是否使用异步模式进行缓存,默认false。 |
Expand All @@ -31,13 +32,13 @@ public Result<?> get(Long id) {
### @CachePut(更新缓存)
|属性名 |作用与描述 |
|-- |-- |
|cacheNames/value |指定缓存的名字,缓存使用CacheManager管理多个缓存Cache,这些Cache就是根据该属性进行区分。对缓存的真正增删改查操作在Cache中定义,每个缓存Cache都有自己唯一的名字。 |
|key |缓存数据时的key的值,默认是使用方法所有入参的值,可以使用SpEL表达式表示key的值。 |
|keyGenerator |缓存的生成策略(键生成器),和key二选一,作用是生成键值key,keyGenerator可自定义。 |
|cacheManager |指定缓存管理器(例如ConcurrentHashMap、Redis等) |
|cacheResolver |和cacheManager作用一样,使用时二选一 |
|condition |指定缓存的条件(对参数判断,满足什么条件时才缓存),可用SpEL表达式,例如:方法入参为对象user则表达式可以写为condition = "#user.age>18",表示当入参对象user的属性age大于18才进行缓存。 |
|unless |否定缓存的条件(对结果判断,满足什么条件时不缓存),即满足unless指定的条件时,对调用方法获取的结果不进行缓存,例如:unless = "result==null",表示如果结果为null时不缓存。 |
|cacheNames/value |缓存名(必须指定至少一个值),即缓存命名空间名称,用于确定缓存目标。 |
|key |缓存key,缺省为按照方法的所有入参进行组合,可以使用SpEL表达式,实际存储key时默认以`cacheNames/value`作为前缀。 |
|keyGenerator |指定key的生成器生成键值key(与`key`二选一),非必需。 |
|cacheManager |指定缓存管理器(例如ConcurrentHashMap、Redis等),非必需。 |
|cacheResolver |和cacheManager作用一样,使用时二选一,非必需。 |
|condition |指定缓存的条件(对参数判断,满足什么条件时才缓存),可用SpEL表达式,例如:方法入参为对象user则表达式可以写为`condition = "#user.age>18"`,表示当入参对象user的属性age大于18才进行缓存。|
|unless |否定缓存的条件(对结果判断,满足什么条件时不缓存),即满足unless指定的条件时,对调用方法获取的结果不进行缓存,例如:`unless = "result==null"`,表示如果结果为null时不缓存。 |

`cache_test`作为缓存名,参数`id`作为缓存key,当方法逻辑执行完之后,将返回结果进行覆盖缓存。
```java
Expand All @@ -51,13 +52,13 @@ public Result<?> put(@Validated UserGroupIPO userGroupIPO) {

### @CacheEvict(删除缓存)
|属性名 |作用与描述 |
|-- |-- |
|cacheNames/value |指定缓存的名字,缓存使用CacheManager管理多个缓存Cache,这些Cache就是根据该属性进行区分。对缓存的真正增删改查操作在Cache中定义,每个缓存Cache都有自己唯一的名字。 |
|key |缓存数据时的key的值,默认是使用方法所有入参的值,可以使用SpEL表达式表示key的值。 |
|keyGenerator |缓存的生成策略(键生成器),和key二选一,作用是生成键值key,keyGenerator可自定义。 |
|cacheManager |指定缓存管理器(例如ConcurrentHashMap、Redis等) |
|cacheResolver |和cacheManager作用一样,使用时二选一 |
|condition |指定删除缓存的条件(对参数判断,满足什么条件时才删除缓存),可用SpEL表达式,例如:入参为字符userId的方法删除缓存条件设定为当入参不是user001就删除缓存,则表达式可以写为condition = "!('user001').equals(#userId)"。 |
|-- |-- |
|cacheNames/value |缓存名(必须指定至少一个值),即缓存命名空间名称,用于确定缓存目标。 |
|key |缓存key,缺省为按照方法的所有入参进行组合,可以使用SpEL表达式,实际存储key时默认以`cacheNames/value`作为前缀。 |
|keyGenerator |指定key的生成器生成键值key(与`key`二选一),非必需。 |
|cacheManager |指定缓存管理器(例如ConcurrentHashMap、Redis等),非必需。 |
|cacheResolver |和cacheManager作用一样,使用时二选一,非必需。 |
|condition |指定删除缓存的条件(对参数判断,满足什么条件时才删除缓存),可用SpEL表达式,例如:入参为字符userId的方法删除缓存条件设定为当入参不是user001就删除缓存,则表达式可以写为`condition = "!('user001').equals(#userId)"`|
|allEntries |allEntries是布尔类型的,用来表示是否需要清除缓存中的所有元素。默认值为false,表示不需要。当指定allEntries为true时,Spring Cache将忽略指定的key,清除缓存中的所有内容。 |
|beforeInvocation |清除操作默认是在对应方法执行成功后触发的(beforeInvocation = false),即方法如果因为抛出异常而未能成功返回时则不会触发清除操作。使用beforeInvocation属性可以改变触发清除操作的时间。当指定该属性值为true时,Spring会在调用该方法之前清除缓存中的指定元素。 |

Expand All @@ -71,16 +72,112 @@ public Result<?> delete(@RequestParam("id") Long id) {
}
```

### @EnableCaching
### @CacheConfig
注解在类上面,可使`@Cacheable``@CachePut``@CacheEvict`注解无需定义重复的:`cacheNames/value、keyGenerator、cacheManager、cacheResolver`属性值
```java
@CacheConfig(cacheNames = "cache_test")
@RestController
@RequestMapping("/cache")
public class CacheController {

@Autowired
JdbcDAO jdbcDAO;

@Cacheable(key = "#id") // 无需重复指定 cacheNames/value 属性
@GetMapping("/get")
public Result<?> get(Long id) {
return R.success(jdbcDAO.get(id));
}

}
```

### @Caching
用于将`@Cacheable、@CachePut、@CacheEvict`这三个注解所提供的能力自由组合,如查询用户时缓存不应该只放id → user,应该连同 cellphone → user、email → user一起放入,这样下次如果按照cellphone或email来查询时,也可从缓存中命中了。
```java
@Caching(
cacheable = {
@Cacheable(value = "cache_test", key = "#id")
},
put = {
@CachePut(value = "cache_test", key = "#result.cellphone", condition = "#result != null"),
@CachePut(value = "cache_test", key = "#result.email", condition = "#result != null")
}
)
public UserDO getUserDO(Long id) {
return jdbcDAO.get(id);
}
```

## 开始使用
在启动类或配置类加上`@EnableCaching`注解,表示启用缓存,使上面介绍的注解生效。
### 启用缓存
在启动类中加上`@EnableCaching`注解开启缓存
```java
@EnableCaching
@SpringBootApplication
public class TestApplication {

public static void main(String[] args) throws Exception {
SpringApplication.run(TestApplication.class, args);
}

}
```

### 使用缓存
```java
@Cacheable(value = "cache_test", key = "#id")
@GetMapping("/get")
public Result<?> get(Long id) {
System.out.println("未命中Redis缓存,使用JDBC去数据库查询数据。");
return R.success(jdbcDAO.get(id));
}
```

### 测试
http://localhost:8080/cache/get?id=18

**第一次访问-控制台打印结果:**
```log
2021-06-05 19:15:34.041 INFO 44320 --- [nio-8080-exec-1] ai.yue.library.test.aspect.HttpAspect : requestIp=0:0:0:0:0:0:0:1
2021-06-05 19:15:34.041 INFO 44320 --- [nio-8080-exec-1] ai.yue.library.test.aspect.HttpAspect : requestUri=/cache/get
2021-06-05 19:15:34.041 INFO 44320 --- [nio-8080-exec-1] ai.yue.library.test.aspect.HttpAspect : requestMethod=GET
2021-06-05 19:15:34.041 INFO 44320 --- [nio-8080-exec-1] ai.yue.library.test.aspect.HttpAspect : requestHandlerMethod=ai.yue.library.test.controller.data.redis.CacheController.get()
未命中Redis缓存,使用JDBC去数据库查询数据。
2021-06-05 19:54:11.332 DEBUG 44320 --- [nio-8080-exec-4] druid.sql.Statement : {conn-310002, pstmt-320001} executed.
select * from `user` where 1 = 1 and `id` = 18
```

**第一次访问-响应数据:**
```json
{
"code": 200,
"msg": "成功",
"flag": true,
"count": null,
"data": {
"id": 18,
"sortIdx": null,
"deleteTime": 0,
"createTime": "2018-07-18 09:10:46",
"updateTime": "2021-06-03 14:29:41",
"cellphone": "185****6311",
"email": null,
"password": "***********************************************",
"nickname": "张三丰3",
"sex": ""
}
}
```

**第二次访问:结果与第一次相同,但未打印出查询日志,因此证明响应的结果是取的Redis缓存数据,而不是执行的JDBC查询。同时我们也确认下Redis中是否有缓存数据:**

使用`@CacheConfig`注解,可以在类上
![确认Redis缓存](redis_files/1.jpg)

### 维护缓存
上面我们介绍了如何使用`@Cacheable`注解添加与获取缓存,实际场景中我们还需要更新缓存`@CachePut`与删除缓存`@CacheEvict`
开发者需要结合业务情况,在需要操作到缓存相关数据时,进行缓存数据同步,也就是更新或删除缓存,需求多变灵活运用。

### 注解使用
上面介绍了注解的简单使用,在真实场景下需求多变,注解也同样需要进行灵活使用,结合`condition、unless`等条件可满足绝大部分场景下的缓存需求
## 扩展资料
> [👉示例源码](https://gitee.com/yl-yue/yue-library/tree/master/yue-library-samples/yue-library-test)<br>
> [👉Redis缓存雪崩、击穿、穿透、预热、降级详解](https://mp.weixin.qq.com/s/O8eedi3X2TSeeUI6iWF-wA)
24 changes: 0 additions & 24 deletions docs/data/redis/分布式锁.md

This file was deleted.

Loading

0 comments on commit 98989bc

Please sign in to comment.