mirror of
https://github.com/Snailclimb/JavaGuide
synced 2025-08-01 16:28:03 +08:00
Compare commits
10 Commits
546c7c2f1f
...
9cb8afd4bb
Author | SHA1 | Date | |
---|---|---|---|
|
9cb8afd4bb | ||
|
685472f54c | ||
|
611c015aeb | ||
|
7bc7370d4b | ||
|
c0535df01c | ||
|
43641a81f9 | ||
|
c33993496c | ||
|
af341e3b05 | ||
|
ee1e7a5140 | ||
|
e29ad07899 |
@ -211,14 +211,25 @@ HTTP/2.0 多路复用效果图(图源: [HTTP/2 For Web Developers](https://b
|
||||
|
||||
- **传输协议**:HTTP/2.0 是基于 TCP 协议实现的,HTTP/3.0 新增了 QUIC(Quick UDP Internet Connections) 协议来实现可靠的传输,提供与 TLS/SSL 相当的安全性,具有较低的连接和传输延迟。你可以将 QUIC 看作是 UDP 的升级版本,在其基础上新增了很多功能比如加密、重传等等。HTTP/3.0 之前名为 HTTP-over-QUIC,从这个名字中我们也可以发现,HTTP/3 最大的改造就是使用了 QUIC。
|
||||
- **连接建立**:HTTP/2.0 需要经过经典的 TCP 三次握手过程(由于安全的 HTTPS 连接建立还需要 TLS 握手,共需要大约 3 个 RTT)。由于 QUIC 协议的特性(TLS 1.3,TLS 1.3 除了支持 1 个 RTT 的握手,还支持 0 个 RTT 的握手)连接建立仅需 0-RTT 或者 1-RTT。这意味着 QUIC 在最佳情况下不需要任何的额外往返时间就可以建立新连接。
|
||||
- **头部压缩**:HTTP/2.0 使用 HPACK 算法进行头部压缩,而 HTTP/3.0 使用更高效的 QPACK 头压缩算法。
|
||||
- **队头阻塞**:HTTP/2.0 多请求复用一个 TCP 连接,一旦发生丢包,就会阻塞住所有的 HTTP 请求。由于 QUIC 协议的特性,HTTP/3.0 在一定程度上解决了队头阻塞(Head-of-Line blocking, 简写:HOL blocking)问题,一个连接建立多个不同的数据流,这些数据流之间独立互不影响,某个数据流发生丢包了,其数据流不受影响(本质上是多路复用+轮询)。
|
||||
- **连接迁移**:HTTP/3.0 支持连接迁移,因为 QUIC 使用 64 位 ID 标识连接,只要 ID 不变就不会中断,网络环境改变时(如从 Wi-Fi 切换到移动数据)也能保持连接。而 TCP 连接是由(源 IP,源端口,目的 IP,目的端口)组成,这个四元组中一旦有一项值发生改变,这个连接也就不能用了。
|
||||
- **错误恢复**:HTTP/3.0 具有更好的错误恢复机制,当出现丢包、延迟等网络问题时,可以更快地进行恢复和重传。而 HTTP/2.0 则需要依赖于 TCP 的错误恢复机制。
|
||||
- **安全性**:HTTP/2.0 和 HTTP/3.0 在安全性上都有较高的要求,支持加密通信,但在实现上有所不同。HTTP/2.0 使用 TLS 协议进行加密,而 HTTP/3.0 基于 QUIC 协议,包含了内置的加密和身份验证机制,可以提供更强的安全性。
|
||||
- **安全性**:在 HTTP/2.0 中,TLS 用于加密和认证整个 HTTP 会话,包括所有的 HTTP 头部和数据负载。TLS的工作是在 TCP 层之上,它加密的是在 TCP 连接中传输的应用层的数据,并不会对 TCP 头部以及 TLS 记录层头部进行加密,所以在传输的过程中 TCP 头部可能会被攻击者篡改来干扰通信。而 HTTP/3.0 的 QUIC 对整个数据包(包括报文头和报文体)进行了加密与认证处理,保障安全性。
|
||||
|
||||
HTTP/1.0、HTTP/2.0 和 HTTP/3.0 的协议栈比较:
|
||||
|
||||

|
||||
|
||||
下图是一个更详细的 HTTP/2.0 和 HTTP/3.0 对比图:
|
||||
|
||||

|
||||
|
||||
从上图可以看出:
|
||||
|
||||
- **HTTP/2.0**:使用 TCP 作为传输协议、使用 HPACK 进行头部压缩、依赖 TLS 进行加密。
|
||||
- **HTTP/3.0**:使用基于 UDP 的 QUIC 协议、使用更高效的 QPACK 进行头部压缩、在 QUIC 中直接集成了 TLS。QUIC 协议具备连接迁移、拥塞控制与避免、流量控制等特性。
|
||||
|
||||
关于 HTTP/1.0 -> HTTP/3.0 更详细的演进介绍,推荐阅读[HTTP1 到 HTTP3 的工程优化](https://dbwu.tech/posts/http_evolution/)。
|
||||
|
||||
### HTTP 是不保存状态的协议, 如何保存用户状态?
|
||||
|
@ -88,7 +88,7 @@ SELECT * FROM tb1 WHERE id < 500;
|
||||
|
||||
AVL 树是计算机科学中最早被发明的自平衡二叉查找树,它的名称来自于发明者 G.M. Adelson-Velsky 和 E.M. Landis 的名字缩写。AVL 树的特点是保证任何节点的左右子树高度之差不超过 1,因此也被称为高度平衡二叉树,它的查找、插入和删除在平均和最坏情况下的时间复杂度都是 O(logn)。
|
||||
|
||||

|
||||

|
||||
|
||||
AVL 树采用了旋转操作来保持平衡。主要有四种旋转操作:LL 旋转、RR 旋转、LR 旋转和 RL 旋转。其中 LL 旋转和 RR 旋转分别用于处理左左和右右失衡,而 LR 旋转和 RL 旋转则用于处理左右和右左失衡。
|
||||
|
||||
|
@ -341,15 +341,13 @@ InnoDB 使用缓冲池(Buffer Pool)缓存数据页和索引页,MyISAM 使
|
||||
|
||||
### MyISAM 和 InnoDB 如何选择?
|
||||
|
||||
大多数时候我们使用的都是 InnoDB 存储引擎,在某些读密集的情况下,使用 MyISAM 也是合适的。不过,前提是你的项目不介意 MyISAM 不支持事务、崩溃恢复等缺点(可是~我们一般都会介意啊!)。
|
||||
大多数时候我们使用的都是 InnoDB 存储引擎,在某些读密集的情况下,使用 MyISAM 也是合适的。不过,前提是你的项目不介意 MyISAM 不支持事务、崩溃恢复等缺点(可是~我们一般都会介意啊)。
|
||||
|
||||
《MySQL 高性能》上面有一句话这样写到:
|
||||
|
||||
> 不要轻易相信“MyISAM 比 InnoDB 快”之类的经验之谈,这个结论往往不是绝对的。在很多我们已知场景中,InnoDB 的速度都可以让 MyISAM 望尘莫及,尤其是用到了聚簇索引,或者需要访问的数据都可以放入内存的应用。
|
||||
|
||||
一般情况下我们选择 InnoDB 都是没有问题的,但是某些情况下你并不在乎可扩展能力和并发能力,也不需要事务支持,也不在乎崩溃后的安全恢复问题的话,选择 MyISAM 也是一个不错的选择。但是一般情况下,我们都是需要考虑到这些问题的。
|
||||
|
||||
因此,对于咱们日常开发的业务系统来说,你几乎找不到什么理由再使用 MyISAM 作为自己的 MySQL 数据库的存储引擎。
|
||||
因此,对于咱们日常开发的业务系统来说,你几乎找不到什么理由使用 MyISAM 了,老老实实用默认的 InnoDB 就可以了!
|
||||
|
||||
## MySQL 索引
|
||||
|
||||
@ -650,7 +648,7 @@ SELECT ... FOR UPDATE;
|
||||
- **意向共享锁(Intention Shared Lock,IS 锁)**:事务有意向对表中的某些记录加共享锁(S 锁),加共享锁前必须先取得该表的 IS 锁。
|
||||
- **意向排他锁(Intention Exclusive Lock,IX 锁)**:事务有意向对表中的某些记录加排他锁(X 锁),加排他锁之前必须先取得该表的 IX 锁。
|
||||
|
||||
**意向锁是由数据引擎自己维护的,用户无法手动操作意向锁,在为数据行加共享/排他锁之前,InooDB 会先获取该数据行所在在数据表的对应意向锁。**
|
||||
**意向锁是由数据引擎自己维护的,用户无法手动操作意向锁,在为数据行加共享/排他锁之前,InnoDB 会先获取该数据行所在在数据表的对应意向锁。**
|
||||
|
||||
意向锁之间是互相兼容的。
|
||||
|
||||
|
@ -23,7 +23,7 @@ head:
|
||||
1. **时间维度区分**:按照数据的创建时间、更新时间、过期时间等,将一定时间段内的数据视为热数据,超过该时间段的数据视为冷数据。例如,订单系统可以将 1 年前的订单数据作为冷数据,1 年内的订单数据作为热数据。这种方法适用于数据的访问频率和时间有较强的相关性的场景。
|
||||
2. **访问频率区分**:将高频访问的数据视为热数据,低频访问的数据视为冷数据。例如,内容系统可以将浏览量非常低的文章作为冷数据,浏览量较高的文章作为热数据。这种方法需要记录数据的访问频率,成本较高,适合访问频率和数据本身有较强的相关性的场景。
|
||||
|
||||
几年前的数据并不一定都是热数据,例如一些优质文章发表几年后依然有很多人访问,大部分普通用户新发表的文章却基本没什么人访问。
|
||||
几年前的数据并不一定都是冷数据,例如一些优质文章发表几年后依然有很多人访问,大部分普通用户新发表的文章却基本没什么人访问。
|
||||
|
||||
这两种区分冷热数据的方法各有优劣,实际项目中,可以将两者结合使用。
|
||||
|
||||
|
@ -305,8 +305,11 @@ public class ArrayList<E> extends AbstractList<E>
|
||||
|
||||
/**
|
||||
* 以正确的顺序(从第一个到最后一个元素)返回一个包含此列表中所有元素的数组。
|
||||
* 返回的数组将是“安全的”,因为该列表不保留对它的引用。 (换句话说,这个方法必须分配一个新的数组)。
|
||||
* 因此,调用者可以自由地修改返回的数组。 此方法充当基于阵列和基于集合的API之间的桥梁。
|
||||
* 返回的数组将是“安全的”,因为该列表不保留对它的引用。
|
||||
* (换句话说,这个方法必须分配一个新的数组)。
|
||||
* 因此,调用者可以自由地修改返回的数组结构。
|
||||
* 注意:如果元素是引用类型,修改元素的内容会影响到原列表中的对象。
|
||||
* 此方法充当基于数组和基于集合的API之间的桥梁。
|
||||
*/
|
||||
public Object[] toArray() {
|
||||
return Arrays.copyOf(elementData, size);
|
||||
|
@ -48,18 +48,18 @@ head:
|
||||
下面这个方法保证了 `HashMap` 总是使用 2 的幂作为哈希表的大小。
|
||||
|
||||
```java
|
||||
/**
|
||||
* Returns a power of two size for the given target capacity.
|
||||
*/
|
||||
static final int tableSizeFor(int cap) {
|
||||
int n = cap - 1;
|
||||
n |= n >>> 1;
|
||||
n |= n >>> 2;
|
||||
n |= n >>> 4;
|
||||
n |= n >>> 8;
|
||||
n |= n >>> 16;
|
||||
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
|
||||
}
|
||||
/**
|
||||
* Returns a power of two size for the given target capacity.
|
||||
*/
|
||||
static final int tableSizeFor(int cap) {
|
||||
int n = cap - 1;
|
||||
n |= n >>> 1;
|
||||
n |= n >>> 2;
|
||||
n |= n >>> 4;
|
||||
n |= n >>> 8;
|
||||
n |= n >>> 16;
|
||||
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
|
||||
}
|
||||
```
|
||||
|
||||
### HashMap 和 HashSet 区别
|
||||
@ -185,7 +185,7 @@ final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
|
||||
|
||||
JDK1.8 之前 `HashMap` 底层是 **数组和链表** 结合在一起使用也就是 **链表散列**。HashMap 通过 key 的 `hashcode` 经过扰动函数处理过后得到 hash 值,然后通过 `(n - 1) & hash` 判断当前元素存放的位置(这里的 n 指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。
|
||||
|
||||
所谓扰动函数指的就是 HashMap 的 `hash` 方法。使用 `hash` 方法也就是扰动函数是为了防止一些实现比较差的 `hashCode()` 方法 换句话说使用扰动函数之后可以减少碰撞。
|
||||
`HashMap` 中的扰动函数(`hash` 方法)是用来优化哈希值的分布。通过对原始的 `hashCode()` 进行额外处理,扰动函数可以减小由于糟糕的 `hashCode()` 实现导致的碰撞,从而提高数据的分布均匀性。
|
||||
|
||||
**JDK 1.8 HashMap 的 hash 方法源码:**
|
||||
|
||||
@ -286,11 +286,55 @@ final void treeifyBin(Node<K,V>[] tab, int hash) {
|
||||
|
||||
### HashMap 的长度为什么是 2 的幂次方
|
||||
|
||||
为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀。我们上面也讲到了过了,Hash 值的范围值-2147483648 到 2147483647,前后加起来大概 40 亿的映射空间,只要哈希函数映射得比较均匀松散,一般应用是很难出现碰撞的。但问题是一个 40 亿长度的数组,内存是放不下的。所以这个散列值是不能直接拿来用的。用之前还要先做对数组的长度取模运算,得到的余数才能用来要存放的位置也就是对应的数组下标。
|
||||
为了让 `HashMap` 存取高效并减少碰撞,我们需要确保数据尽量均匀分布。哈希值在 Java 中通常使用 `int` 表示,其范围是 `-2147483648 ~ 2147483647`前后加起来大概 40 亿的映射空间,只要哈希函数映射得比较均匀松散,一般应用是很难出现碰撞的。但是,问题是一个 40 亿长度的数组,内存是放不下的。所以,这个散列值是不能直接拿来用的。用之前还要先做对数组的长度取模运算,得到的余数才能用来要存放的位置也就是对应的数组下标。
|
||||
|
||||
**这个算法应该如何设计呢?**
|
||||
|
||||
我们首先可能会想到采用 % 取余的操作来实现。但是,重点来了:“**取余(%)操作中如果除数是 2 的幂次则等价于与其除数减一的与(&)操作**(也就是说 hash%length==hash&(length-1)的前提是 length 是 2 的 n 次方;)。” 并且 **采用二进制位操作 & 相对于 % 能够提高运算效率**,这就解释了 HashMap 的长度为什么是 2 的幂次方。
|
||||
我们首先可能会想到采用 % 取余的操作来实现。但是,重点来了:“**取余(%)操作中如果除数是 2 的幂次则等价于与其除数减一的与(&)操作**(也就是说 `hash%length==hash&(length-1)` 的前提是 length 是 2 的 n 次方)。” 并且,**采用二进制位操作 & 相对于 % 能够提高运算效率**。
|
||||
|
||||
除了上面所说的位运算比取余效率高之外,我觉得更重要的一个原因是:**长度是 2 的幂次方,可以让 `HashMap` 在扩容的时候更均匀**。例如:
|
||||
|
||||
- length = 8 时,length - 1 = 7 的二进制位`0111`
|
||||
- length = 16 时,length - 1 = 15 的二进制位`1111`
|
||||
|
||||
这时候原本存在 `HashMap` 中的元素计算新的数组位置时 `hash&(length-1)`,取决 hash 的第四个二进制位(从右数),会出现两种情况:
|
||||
|
||||
1. 第四个二进制位为 0,数组位置不变,也就是说当前元素在新数组和旧数组的位置相同。
|
||||
2. 第四个二进制位为 1,数组位置在新数组扩容之后的那一部分。
|
||||
|
||||
这里列举一个例子:
|
||||
|
||||
```plain
|
||||
假设有一个元素的哈希值为 10101100
|
||||
|
||||
旧数组元素位置计算:
|
||||
hash = 10101100
|
||||
length - 1 = 00000111
|
||||
& -----------------
|
||||
index = 00000100 (4)
|
||||
|
||||
新数组元素位置计算:
|
||||
hash = 10101100
|
||||
length - 1 = 00001111
|
||||
& -----------------
|
||||
index = 00001100 (12)
|
||||
|
||||
看第四位(从右数):
|
||||
1.高位为 0:位置不变。
|
||||
2.高位为 1:移动到新位置(原索引位置+原容量)。
|
||||
```
|
||||
|
||||
⚠️注意:这里列举的场景看的是第四个二进制位,更准确点来说看的是高位(从右数),例如 `length = 32` 时,`length - 1 = 31`,二进制为 `11111`,这里看的就是第五个二进制位。
|
||||
|
||||
也就是说扩容之后,在旧数组元素 hash 值比较均匀(至于 hash 值均不均匀,取决于前面讲的对象的 `hashcode()` 方法和扰动函数)的情况下,新数组元素也会被分配的比较均匀,最好的情况是会有一半在新数组的前半部分,一半在新数组后半部分。
|
||||
|
||||
这样也使得扩容机制变得简单和高效,扩容后只需检查哈希值高位的变化来决定元素的新位置,要么位置不变(高位为 0),要么就是移动到新位置(高位为 1,原索引位置+原容量)。
|
||||
|
||||
最后,简单总结一下 `HashMap` 的长度是 2 的幂次方的原因:
|
||||
|
||||
1. 位运算效率更高:位运算(&)比取余运算(%)更高效。当长度为 2 的幂次方时,`hash % length` 等价于 `hash & (length - 1)`。
|
||||
2. 可以更好地保证哈希值的均匀分布:扩容之后,在旧数组元素 hash 值比较均匀的情况下,新数组元素也会被分配的比较均匀,最好的情况是会有一半在新数组的前半部分,一半在新数组后半部分。
|
||||
3. 扩容机制变得简单和高效:扩容后只需检查哈希值高位的变化来决定元素的新位置,要么位置不变(高位为 0),要么就是移动到新位置(高位为 1,原索引位置+原容量)。
|
||||
|
||||
### HashMap 多线程操作导致死循环问题
|
||||
|
||||
|
@ -125,9 +125,8 @@ icon: "xitongsheji"
|
||||
- [Quartz](https://github.com/quartz-scheduler/quartz):一个很火的开源任务调度框架,Java 定时任务领域的老大哥或者说参考标准, 很多其他任务调度框架都是基于 `quartz` 开发的,比如当当网的`elastic-job`就是基于`quartz`二次开发之后的分布式调度解决方案
|
||||
- [XXL-JOB](https://github.com/xuxueli/xxl-job) :XXL-JOB 是一个分布式任务调度平台,其核心设计目标是开发迅速、学习简单、轻量级、易扩展。现已开放源代码并接入多家公司线上产品线,开箱即用。
|
||||
- [Elastic-Job](http://elasticjob.io/index_zh.html):Elastic-Job 是当当网开源的一个基于 Quartz 和 Zookeeper 的分布式调度解决方案,由两个相互独立的子项目 Elastic-Job-Lite 和 Elastic-Job-Cloud 组成,一般我们只要使用 Elastic-Job-Lite 就好。
|
||||
- [EasyScheduler](https://github.com/analysys/EasyScheduler "EasyScheduler") (已经更名为 DolphinScheduler,已经成为 Apache 孵化器项目):Easy Scheduler 是一个分布式工作流任务调度系统,主要解决“复杂任务依赖但无法直接监控任务健康状态”的问题。Easy Scheduler 以 DAG 方式组装任务,可以实时监控任务的运行状态。同时,它支持重试,重新运行等操作... 。
|
||||
- [EasyScheduler](https://github.com/analysys/EasyScheduler "EasyScheduler") (已经更名为 DolphinScheduler,已经成为 Apache 孵化器项目):分布式易扩展的可视化工作流任务调度平台,主要解决“复杂任务依赖但无法直接监控任务健康状态”的问题。
|
||||
- [PowerJob](https://gitee.com/KFCFans/PowerJob):新一代分布式任务调度与计算框架,支持 CRON、API、固定频率、固定延迟等调度策略,提供工作流来编排任务解决依赖关系,使用简单,功能强大,文档齐全,欢迎各位接入使用!<http://www.powerjob.tech/> 。
|
||||
- [DolphinScheduler](https://github.com/apache/dolphinscheduler):分布式易扩展的可视化工作流任务调度平台。
|
||||
|
||||
## 分布式
|
||||
|
||||
|
@ -18,7 +18,7 @@ footer: |-
|
||||
|
||||
## 关于网站
|
||||
|
||||
JavaGuide 已经持续维护 5 年多了,累计提交了 **5000+** commit ,共有 **440** 多位朋友参与维护。真心希望能够把这个项目做好,真正能够帮助到有需要的朋友!
|
||||
JavaGuide 已经持续维护 6 年多了,累计提交了 **5500+** commit ,共有 **520+** 多位贡献者共同参与维护和完善。真心希望能够把这个项目做好,真正能够帮助到有需要的朋友!
|
||||
|
||||
如果觉得 JavaGuide 的内容对你有帮助的话,还请点个免费的 Star(绝不强制点 Star,觉得内容不错有收货再点赞就好),这是对我最大的鼓励,感谢各位一路同行,共勉!传送门:[GitHub](https://github.com/Snailclimb/JavaGuide) | [Gitee](https://gitee.com/SnailClimb/JavaGuide)。
|
||||
|
||||
|
@ -94,7 +94,7 @@ AOP 之所以叫面向切面编程,是因为它的核心思想就是将横切
|
||||
- **连接点(JoinPoint)**:连接点是方法调用或者方法执行时的某个特定时刻(如方法调用、异常抛出等)。
|
||||
- **通知(Advice)**:通知就是切面在某个连接点要执行的操作。通知有五种类型,分别是前置通知(Before)、后置通知(After)、返回通知(AfterReturning)、异常通知(AfterThrowing)和环绕通知(Around)。前四种通知都是在目标方法的前后执行,而环绕通知可以控制目标方法的执行过程。
|
||||
- **切点(Pointcut)**:一个切点是一个表达式,它用来匹配哪些连接点需要被切面所增强。切点可以通过注解、正则表达式、逻辑运算等方式来定义。比如 `execution(* com.xyz.service..*(..))`匹配 `com.xyz.service` 包及其子包下的类或接口。
|
||||
- **织入(Weaving)**:织入是将切面和目标对象连接起来的过程,也就是将通知应用到切点匹配的连接点上。常见的织入时机有两种,分别是编译期织入(AspectJ)和运行期织入(AspectJ)。
|
||||
- **织入(Weaving)**:织入是将切面和目标对象连接起来的过程,也就是将通知应用到切点匹配的连接点上。常见的织入时机有两种,分别是编译期织入(Compile-Time Weaving 如:AspectJ)和运行期织入(Runtime Weaving 如:AspectJ、Spring AOP)。
|
||||
|
||||
### AOP 解决了什么问题?
|
||||
|
||||
|
@ -368,38 +368,43 @@ FastJSON 实现数据脱敏的方式主要有两种:
|
||||
- 基于注解 `@JSONField` 实现:需要自定义一个用于脱敏的序列化的类,然后在需要脱敏的字段上通过 `@JSONField` 中的 `serializeUsing` 指定为我们自定义的序列化类型即可。
|
||||
- 基于序列化过滤器:需要实现 `ValueFilter` 接口,重写 `process` 方法完成自定义脱敏,然后在 JSON 转换时使用自定义的转换策略。具体实现可参考这篇文章: <https://juejin.cn/post/7067916686141161479>。
|
||||
|
||||
### Mybatis-mate
|
||||
### Mybatis-Mate
|
||||
|
||||
MybatisPlus 也提供了数据脱敏模块 mybatis-mate。mybatis-mate 为 MybatisPlus 企业级模块,使用之前需要配置授权码(付费),旨在更敏捷优雅处理数据。
|
||||
先介绍一下 MyBatis、MyBatis-Plus 和 Mybatis-Mate 这三者的关系:
|
||||
|
||||
配置内容如下所示:
|
||||
- MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。
|
||||
- MyBatis-Plus 是一个 MyBatis 的增强工具,能够极大地简化持久层的开发工作。
|
||||
- Mybatis-Mate 是为 MyBatis-Plus 提供的企业级模块,旨在更敏捷优雅处理数据。不过,使用之前需要配置授权码(付费)。
|
||||
|
||||
```yaml
|
||||
# Mybatis Mate 配置
|
||||
mybatis-mate:
|
||||
cert:
|
||||
grant: jxftsdfggggx
|
||||
license: GKXP9r4MCJhGID/DTGigcBcLmZjb1YZGjE4GXaAoxbtGsPC20sxpEtiUr2F7Nb1ANTUekvF6Syo6DzraA4M4oacwoLVTglzfvaEfadfsd232485eLJK1QsskrSJmreMnEaNh9lsV7Lpbxy9JeGCeM0HPEbRvq8Y+8dUt5bQYLklsa3ZIBexir+4XykZY15uqn1pYIp4pEK0+aINTa57xjJNoWuBIqm7BdFIb4l1TAcPYMTsMXhF5hfMmKD2h391HxWTshJ6jbt4YqdKD167AgeoM+B+DE1jxlLjcpskY+kFs9piOS7RCcmKBBUOgX2BD/JxhR2gQ==
|
||||
Mybatis-Mate 支持敏感词脱敏,内置手机号、邮箱、银行卡号等 9 种常用脱敏规则。
|
||||
|
||||
```java
|
||||
@FieldSensitive("testStrategy")
|
||||
private String username;
|
||||
|
||||
@Configuration
|
||||
public class SensitiveStrategyConfig {
|
||||
|
||||
/**
|
||||
* 注入脱敏策略
|
||||
*/
|
||||
@Bean
|
||||
public ISensitiveStrategy sensitiveStrategy() {
|
||||
// 自定义 testStrategy 类型脱敏处理
|
||||
return new SensitiveStrategy().addStrategy("testStrategy", t -> t + "***test***");
|
||||
}
|
||||
}
|
||||
|
||||
// 跳过脱密处理,用于编辑场景
|
||||
RequestDataTransfer.skipSensitive();
|
||||
```
|
||||
|
||||
具体实现可参考 baomidou 提供的如下代码:<https://gitee.com/baomidou/mybatis-mate-examples> 。
|
||||
|
||||
### MyBatis-Flex
|
||||
|
||||
类似于 MybatisPlus,MyBatis-Flex 也是一个 MyBatis 增强框架。MyBatis-Flex 同样提供了数据脱敏功能,并且是可以免费使用的。
|
||||
|
||||
MyBatis-Flex 提供了 `@ColumnMask()` 注解,以及内置的 9 种脱敏规则,开箱即用:
|
||||
|
||||
- 用户名脱敏
|
||||
- 手机号脱敏
|
||||
- 固定电话脱敏
|
||||
- 身份证号脱敏
|
||||
- 车牌号脱敏
|
||||
- 地址脱敏
|
||||
- 邮件脱敏
|
||||
- 密码脱敏
|
||||
- 银行卡号脱敏
|
||||
|
||||
```java
|
||||
/**
|
||||
* 内置的数据脱敏方式
|
||||
@ -465,14 +470,57 @@ public class Account {
|
||||
|
||||
如果这些内置的脱敏规则不满足你的要求的话,你还可以自定义脱敏规则。
|
||||
|
||||
1、通过 `MaskManager` 注册新的脱敏规则:
|
||||
|
||||
```java
|
||||
MaskManager.registerMaskProcessor("自定义规则名称"
|
||||
, data -> {
|
||||
return data;
|
||||
})
|
||||
```
|
||||
|
||||
2、使用自定义的脱敏规则
|
||||
|
||||
```java
|
||||
@Table("tb_account")
|
||||
public class Account {
|
||||
|
||||
@Id(keyType = KeyType.Auto)
|
||||
private Long id;
|
||||
|
||||
@ColumnMask("自定义规则名称")
|
||||
private String userName;
|
||||
}
|
||||
```
|
||||
|
||||
并且,对于需要跳过脱密处理的场景,例如进入编辑页面编辑用户数据,MyBatis-Flex 也提供了对应的支持:
|
||||
|
||||
1. **`MaskManager#execWithoutMask`**(推荐):该方法使用了模版方法设计模式,保障跳过脱敏处理并执行相关逻辑后自动恢复脱敏处理。
|
||||
2. **`MaskManager#skipMask`**:跳过脱敏处理。
|
||||
3. **`MaskManager#restoreMask`**:恢复脱敏处理,确保后续的操作继续使用脱敏逻辑。
|
||||
|
||||
`MaskManager#execWithoutMask`方法实现如下:
|
||||
|
||||
```java
|
||||
public static <T> T execWithoutMask(Supplier<T> supplier) {
|
||||
try {
|
||||
skipMask();
|
||||
return supplier.get();
|
||||
} finally {
|
||||
restoreMask();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`MaskManager` 的`skipMask`和`restoreMask`方法一般配套使用,推荐`try{...}finally{...}`模式。
|
||||
|
||||
## 总结
|
||||
|
||||
本文主要介绍了数据脱敏的相关内容,首先介绍了数据脱敏的概念,在此基础上介绍了常用的数据脱敏规则;随后介绍了本文的重点 Hutool 工具及其使用方法,在此基础上进行了实操,分别演示了使用 DesensitizedUtil 工具类、配合 Jackson 通过注解的方式完成数据脱敏;最后,介绍了一些常见的数据脱敏方法,并附上了对应的教程链接供大家参考,本文内容如有不当之处,还请大家批评指正。
|
||||
这篇文章主要介绍了:
|
||||
|
||||
## 推荐阅读
|
||||
|
||||
- [Spring Boot 日志、配置文件、接口数据如何脱敏?老鸟们都是这样玩的!](https://mp.weixin.qq.com/s/59osrnjyPJ7BV070x6ABwQ)
|
||||
- [大厂也在用的 6 种数据脱敏方案,严防泄露数据的“内鬼”](https://mp.weixin.qq.com/s/_Dgekk1AJsIx0TTlnH6kUA)
|
||||
- 数据脱敏的定义:数据脱敏是指对某些敏感信息通过脱敏规则进行数据的变形,实现敏感隐私数据的可靠保护。
|
||||
- 常用的脱敏规则:替换、删除、重排、加噪和加密。
|
||||
- 常用的脱敏工具:Hutool、Apache ShardingSphere、FastJSON、Mybatis-Mate 和 MyBatis-Flex。
|
||||
|
||||
## 参考
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user