SpringBoot使用自定義注解實(shí)現(xiàn)數(shù)據(jù)脫敏過(guò)程詳細(xì)解析
前言
對(duì)于某些接口返回的信息,涉及到敏感數(shù)據(jù)的必須進(jìn)行脫敏操作,例如銀行卡號(hào)、身份證號(hào)、手機(jī)號(hào)等,脫敏方式有多種方式??梢孕薷腟QL語(yǔ)句,也可以寫(xiě)硬代碼,也可以修改JSON序列化,這里介紹通過(guò)修改Jackson序列化方式實(shí)現(xiàn)數(shù)據(jù)脫敏。
一、引入hutool工具類(lèi)
maven:
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.5</version>
</dependency>
gradle:
// https://mvnrepository.com/artifact/cn.hutool/hutool-all
implementation group: 'cn.hutool', name: 'hutool-all', version: '5.8.5'
二、定義常用需要脫敏的數(shù)據(jù)類(lèi)型的枚舉
其中 OTHER類(lèi)型為自定義類(lèi)型,需在后面自定義脫敏的長(zhǎng)度等。
package com.iscas.authentication.model.enums;
import lombok.Getter;
/**
*
* @version 1.0
* @since jdk1.8
*/
@Getter
public enum PrivacyTypeEnum {
/**
* 中文名
* */
CHINESE_NAME,
/**
* 固話
* */
FIXED_PHONE,
/**
* 手機(jī)號(hào)
* */
MOBILE_PHONE,
/**
* 住址
* */
ADDRESS,
/**
* 密碼
* */
PASSWORD,
/**
* 銀行卡號(hào)
* */
BANK_CARD,
/**
* 郵箱
* */
EMAIL,
/**
* 身份證
* */
ID_CARD,
/**
* 其他類(lèi)型
* */
OTHER;
}
三、定義脫敏方式枚舉
其中,DEFAULT類(lèi)型時(shí),需要數(shù)據(jù)類(lèi)型為上一步枚舉中除OTHER外的已確定的類(lèi)型,NONE表示不做脫敏,其他類(lèi)型為注釋的意思。
package com.iscas.authentication.model.enums;
/**
*
* @version 1.0
* @since jdk1.8
*/
public enum DesensitizationTypeEnum {
/**
* 默認(rèn)方式
* */
DEFAULT,
/**
* 頭部脫敏
* */
HEAD,
/**
* 尾部脫敏
* */
TAIL,
/**
* 中間脫敏
* */
MIDDLE,
/**
* 頭尾脫敏
* */
HEAD_TAIL,
/**
* 全部脫敏
* */
ALL,
/**
* 不脫敏,相當(dāng)于沒(méi)打這個(gè)注解
* */
NONE;
}
四、自定義脫敏的注解
其中,mode默認(rèn)為DEFAULT,此時(shí)只需要設(shè)置dataType的類(lèi)型為除OTHER外的確定類(lèi)型即可,當(dāng)mode不是DEFAULT或NONE時(shí),根據(jù)不同的類(lèi)型,headNoMaskLen等長(zhǎng)度屬性需要設(shè)置,見(jiàn)上面的注釋的字面意思。
package com.iscas.authentication.annotation;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.iscas.authentication.model.enums.DesensitizationTypeEnum;
import com.iscas.authentication.model.enums.PrivacyTypeEnum;
import com.iscas.authentication.service.DesensitizationSerializer;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 脫敏注解
*
* @version 1.0
* @since jdk1.8
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = DesensitizationSerializer.class)
public @interface Desensitization {
/**
* 脫敏的隱私數(shù)據(jù)類(lèi)型
*/
PrivacyTypeEnum dataType();
/**
* 脫敏方式,默認(rèn)方式不需要定義下面脫敏長(zhǎng)度等信息,根據(jù)脫敏的隱私數(shù)據(jù)類(lèi)型自動(dòng)脫敏
*/
DesensitizationTypeEnum mode() default DesensitizationTypeEnum.DEFAULT;
/**
* 尾部不脫敏的長(zhǎng)度,當(dāng)mode為HEAD或MIDDLE時(shí)使用
*/
int tailNoMaskLen() default 1;
/**
* 頭部不脫敏的長(zhǎng)度,當(dāng)mode為T(mén)AIL或MIDDLE時(shí)使用
*/
int headNoMaskLen() default 1;
/**
* 中間不脫敏的長(zhǎng)度,當(dāng)mode為HEAD_TAIL時(shí)使用
*/
int middleNoMaskLen() default 1;
/**
* 打碼
*/
char maskCode() default '*';
}
五、自定義Jackson的序列化方式
package com.iscas.authentication.service;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.iscas.authentication.annotation.Desensitization;
import com.iscas.authentication.model.enums.DesensitizationTypeEnum;
import com.iscas.authentication.model.enums.PrivacyTypeEnum;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import java.io.IOException;
import java.util.Objects;
/**
* 脫敏序列化類(lèi)
*
* @author zhuquanwen
* @version 1.0
* @date 2023/1/5 9:24
* @since jdk1.8
*/
@AllArgsConstructor
@NoArgsConstructor
public class DesensitizationSerializer extends JsonSerializer<String> implements ContextualSerializer {
private Desensitization desensitization;
@Override
public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(desensitize(s));
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
if (beanProperty != null) {
if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
Desensitization desensitization = beanProperty.getAnnotation(Desensitization.class);
if (desensitization == null) {
desensitization = beanProperty.getContextAnnotation(Desensitization.class);
}
if (desensitization != null) {
return new DesensitizationSerializer(desensitization);
}
}
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
}
return serializerProvider.findNullValueSerializer(null);
}
/**
* 脫敏處理
* */
private String desensitize(String s) {
if (StrUtil.isNotBlank(s)) {
PrivacyTypeEnum dataType = desensitization.dataType();
DesensitizationTypeEnum mode = desensitization.mode();
switch (mode) {
case DEFAULT:
// 默認(rèn)方式,根據(jù)dataType自動(dòng)選擇脫敏方式
s = autoDesensitize(s, dataType);
break;
case HEAD:
// 頭部脫敏
s = headDesensitize(s);
break;
case TAIL:
// 尾部脫敏
s = tailDesensitize(s);
break;
case MIDDLE:
s = middleDesensitize(s);
break;
case HEAD_TAIL:
s = headTailDesensitize(s);
break;
case ALL:
s = allDesensitize(s);
break;
case NONE:
// 不做脫敏
break;
default:
}
}
return s;
}
/**
* 全部脫敏
* */
private String allDesensitize(String s) {
return String.valueOf(desensitization.maskCode()).repeat(s.length());
}
/**
* 頭尾脫敏
* */
private String headTailDesensitize(String s) {
int middleNoMaskLen = desensitization.middleNoMaskLen();
if (middleNoMaskLen >= s.length()) {
// 如果中間不脫敏的長(zhǎng)度大于等于字符串的長(zhǎng)度,不進(jìn)行脫敏
return s;
}
int len = s.length() - middleNoMaskLen;
// 頭部脫敏
int headStart = 0;
int headEnd = len / 2;
s = StrUtil.replace(s, headStart, headEnd, desensitization.maskCode());
// 尾部脫敏
int tailStart = s.length() - (len - len / 2);
int tailEnd = s.length();
return StrUtil.replace(s, tailStart, tailEnd, desensitization.maskCode());
}
/**
* 中間脫敏
* */
private String middleDesensitize(String s) {
int headNoMaskLen = desensitization.headNoMaskLen();
int tailNoMaskLen = desensitization.tailNoMaskLen();
if (headNoMaskLen + tailNoMaskLen >= s.length()) {
// 如果頭部不脫敏的長(zhǎng)度+尾部不脫敏長(zhǎng)度 大于等于字符串的長(zhǎng)度,不進(jìn)行脫敏
return s;
}
int start = headNoMaskLen;
int end = s.length() - tailNoMaskLen;
return StrUtil.replace(s, start, end, desensitization.maskCode());
}
/**
* 尾部脫敏
* */
private String tailDesensitize(String s) {
int headNoMaskLen = desensitization.headNoMaskLen();
if (headNoMaskLen >= s.length()) {
// 如果頭部不脫敏的長(zhǎng)度大于等于字符串的長(zhǎng)度,不進(jìn)行脫敏
return s;
}
int start = headNoMaskLen;
int end = s.length();
return StrUtil.replace(s, start, end, desensitization.maskCode());
}
/**
* 頭部脫敏
* */
private String headDesensitize(String s) {
int tailNoMaskLen = desensitization.tailNoMaskLen();
if (tailNoMaskLen >= s.length()) {
// 如果尾部不脫敏的長(zhǎng)度大于等于字符串的長(zhǎng)度,不進(jìn)行脫敏
return s;
}
int start = 0;
int end = s.length() - tailNoMaskLen;
return StrUtil.replace(s, start, end, desensitization.maskCode());
}
public static void main(String[] args) {
System.out.println(StrUtil.replace("231085198901091813", 2, -10, '#'));
}
/**
* 根據(jù)數(shù)據(jù)類(lèi)型自動(dòng)脫敏
* */
private String autoDesensitize(String s, PrivacyTypeEnum dataType) {
switch (dataType) {
case CHINESE_NAME:
s = DesensitizedUtil.chineseName(s);
break;
case FIXED_PHONE:
s = DesensitizedUtil.fixedPhone(s);
break;
case MOBILE_PHONE:
s = DesensitizedUtil.mobilePhone(s);
break;
case ADDRESS:
s = DesensitizedUtil.address(s, 8);
break;
case PASSWORD:
s = DesensitizedUtil.password(s);
break;
case BANK_CARD:
s = DesensitizedUtil.bankCard(s);
break;
case EMAIL:
s = DesensitizedUtil.email(s);
break;
case ID_CARD:
s = DesensitizedUtil.idCardNum(s, 1, 2);
break;
case OTHER:
// 其他類(lèi)型的不支持以默認(rèn)方式脫敏,直接返回
break;
default:
}
return s;
}
}六、使用
下面是一個(gè)測(cè)試的例子:
package com.iscas.base.biz.test.controller;
import com.iscas.base.biz.desensitization.Desensitization;
import com.iscas.base.biz.desensitization.DesensitizationTypeEnum;
import com.iscas.base.biz.desensitization.PrivacyTypeEnum;
import lombok.Data;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author zhuquanwen
* @version 1.0
* @date 2023/1/6 8:40
* @since jdk1.8
*/
@RestController
@RequestMapping("/test/desensitization")
public class TestDesensitizationController {
@GetMapping
public List<TestModel> test() {
TestModel t1 = new TestModel();
t1.setPassword("123456");
t1.setEmail("zzz@163.com");
t1.setPhone("137654879451");
t1.setFixPhone("0453-4785462");
t1.setBankCard("622648754896457");
t1.setIdCard("245874563214578965");
t1.setName("張王釗");
t1.setAddress("北京市昌平區(qū)xxx街道xxx小區(qū)1-1-101");
t1.setHeadStr("測(cè)試頭部脫敏");
t1.setTailStr("測(cè)試尾部脫敏");
t1.setMiddleStr("測(cè)試中間脫敏");
t1.setHeadTailStr("測(cè)試頭尾脫敏");
t1.setAllStr("測(cè)試全部脫敏");
t1.setNoneStr("測(cè)試不脫敏");
TestModel t2 = new TestModel();
t2.setPassword("iscas123");
t2.setEmail("xwg@sina.com");
t2.setPhone("18547896547");
t2.setFixPhone("010-62268795");
t2.setBankCard("622648754896487");
t2.setIdCard("100412547865478947");
t2.setName("李二麻子");
t2.setAddress("新疆省克拉瑪依市xxx街道xxx小區(qū)1-1-101");
t2.setHeadStr("測(cè)試頭部脫敏");
t2.setTailStr("測(cè)試尾部脫敏");
t2.setMiddleStr("測(cè)試中間脫敏");
t2.setHeadTailStr("測(cè)試頭尾脫敏");
t2.setAllStr("測(cè)試全部脫敏");
t2.setNoneStr("測(cè)試不脫敏");
return new ArrayList<>(){{
add(t1);
add(t2);
}};
}
@Data
private static class TestModel {
/**
* 模擬密碼
* */
@Desensitization(dataType = PrivacyTypeEnum.PASSWORD)
private String password;
/**
* 模擬郵箱
* */
@Desensitization(dataType = PrivacyTypeEnum.EMAIL)
private String email;
/**
* 模擬手機(jī)號(hào)
* */
@Desensitization(dataType = PrivacyTypeEnum.MOBILE_PHONE)
private String phone;
/**
* 模擬座機(jī)
* */
@Desensitization(dataType = PrivacyTypeEnum.FIXED_PHONE)
private String fixPhone;
/**
* 模擬銀行卡
* */
@Desensitization(dataType = PrivacyTypeEnum.BANK_CARD)
private String bankCard;
/**
* 模擬身份證號(hào)
* */
@Desensitization(dataType = PrivacyTypeEnum.ID_CARD)
private String idCard;
/**
* 模擬中文名
* */
@Desensitization(dataType = PrivacyTypeEnum.CHINESE_NAME)
private String name;
/**
* 模擬住址
* */
@Desensitization(dataType = PrivacyTypeEnum.ADDRESS)
private String address;
/**
* 模擬自定義脫敏-頭部脫敏
* */
@Desensitization(dataType = PrivacyTypeEnum.OTHER, mode = DesensitizationTypeEnum.HEAD, tailNoMaskLen = 4)
private String headStr;
/**
* 模擬自定義脫敏-尾部脫敏
* */
@Desensitization(dataType = PrivacyTypeEnum.OTHER, mode = DesensitizationTypeEnum.TAIL, headNoMaskLen = 4)
private String tailStr;
/**
* 模擬自定義脫敏-中間脫敏
* */
@Desensitization(dataType = PrivacyTypeEnum.OTHER, mode = DesensitizationTypeEnum.MIDDLE, headNoMaskLen = 2, tailNoMaskLen = 2)
private String middleStr;
/**
* 模擬自定義脫敏-兩頭脫敏
* */
@Desensitization(dataType = PrivacyTypeEnum.OTHER, mode = DesensitizationTypeEnum.HEAD_TAIL, middleNoMaskLen = 4)
private String headTailStr;
/**
* 模擬自定義脫敏-全部脫敏
* */
@Desensitization(dataType = PrivacyTypeEnum.OTHER, mode = DesensitizationTypeEnum.ALL)
private String allStr;
/**
* 模擬自定義脫敏-不脫敏
* */
@Desensitization(dataType = PrivacyTypeEnum.OTHER, mode = DesensitizationTypeEnum.NONE)
private String noneStr;
}
}下面是一個(gè)實(shí)際使用的例子如下,在tel、password、email上添加了@Desensitization注解,自定義的@TbField等注解請(qǐng)忽略
package com.iscas.authentication.model.sys;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.iscas.authentication.annotation.Desensitization;
import com.iscas.authentication.model.enums.PrivacyTypeEnum;
import com.iscas.templet.annotation.table.TbField;
import com.iscas.templet.annotation.table.TbFieldRule;
import com.iscas.templet.annotation.table.TbSetting;
import com.iscas.templet.view.table.TableFieldType;
import com.iscas.templet.view.table.TableSearchType;
import com.iscas.templet.view.table.TableViewType;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.Accessors;
import java.util.List;
/**
* @author zhuquanwen
* @version 1.0
* @date 2022/3/11 21:23
* @since jdk11
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Schema(title = "用戶")
@TableName(value = "oauth_sys_user")
@Accessors(chain = true)
@TbSetting(title = "用戶", checkbox = true, viewType = TableViewType.multi)
public class User extends BaseEntity {
@TableId(type = IdType.AUTO)
@Schema(title = "id")
@TbField(field = "id", header = "id",
type = TableFieldType.text, hidden = true)
private Integer id;
@Schema(title = "用戶名")
@TbField(field = "name", header = "名稱", search = true, searchType = TableSearchType.like,
type = TableFieldType.text, rule=@TbFieldRule(required = true, minLength = 2, maxLength = 20, distinct = true, desc = "用戶名不能為空,且長(zhǎng)度介于2-20個(gè)字符之間"))
private String name;
@Schema(title = "密碼")
@TbField(field = "password", header = "密碼", hidden = true, editable = false,
type = TableFieldType.text)
@Desensitization(dataType = PrivacyTypeEnum.PASSWORD)
private String password;
@Schema(title = "type")
@TbField(field = "type", header = "用戶類(lèi)型", search = true, searchType = TableSearchType.exact,
type = TableFieldType.select, option = "[{\"label\":\"正常用戶\",\"value\":\"1\"},{\"label\":\"戰(zhàn)位IP用戶\",\"value\":\"2\"}]")
private String type;
@Schema(title = "status")
@TbField(field = "status", header = "狀態(tài)", search = true, searchType = TableSearchType.exact,
type = TableFieldType.select, option = "[{\"label\":\"正常\",\"value\":\"1\"},{\"label\":\"禁用\",\"value\":\"0\"}]")
private String status;
@Schema(title = "真實(shí)姓名")
@TbField(field = "realName", header = "真實(shí)姓名",
type = TableFieldType.text, rule=@TbFieldRule(required = true, minLength = 2, maxLength = 20, desc = "真實(shí)姓名不能為空,且長(zhǎng)度介于2-20個(gè)字符之間"))
private String realName;
@Schema(title = "電話號(hào)碼")
@TbField(field = "tel", header = "電話號(hào)碼",
type = TableFieldType.text, rule=@TbFieldRule(reg = "^(13[0-9]|14[01456879]|15[0-3,5-9]|16[2567]|17[0-8]|18[0-9]|19[0-3,5-9])\\d{8}$", desc = "電話號(hào)碼需符規(guī)則"))
@Desensitization(dataType = PrivacyTypeEnum.MOBILE_PHONE)
private String tel;
@Schema(title = "郵箱")
@TbField(field = "email", header = "郵箱",
type = TableFieldType.text, rule=@TbFieldRule(reg = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*.\\w+([-.]\\w+)*$", desc = "郵箱需符規(guī)則"))
@Desensitization(dataType = PrivacyTypeEnum.EMAIL)
private String email;
@Schema(title = "部門(mén)")
@TbField(field = "orgIds", header = "部門(mén)",
type = TableFieldType.multiSelect, selectUrl = "/api/v1/orgs/combobox/tree?status=1")
@TableField(exist = false)
private List<Integer> orgIds;
@Schema(title = "角色")
@TbField(field = "roleIds", header = "角色",
type = TableFieldType.multiSelect, selectUrl = "/api/v1/roles/combobox?status=1")
@TableField(exist = false)
private List<Integer> roleIds;
@Schema(title = "崗位")
@TbField(field = "postIds", header = "崗位",
type = TableFieldType.multiSelect, selectUrl = "/api/v1/posts/combobox?status=1")
@TableField(exist = false)
private List<Integer> postIds;
}七、脫敏效果
下面是測(cè)試的結(jié)果:

下面是一個(gè)查詢接口返回帶User實(shí)體的結(jié)果:

到此這篇關(guān)于SpringBoot使用自定義注解實(shí)現(xiàn)數(shù)據(jù)脫敏過(guò)程詳細(xì)解析的文章就介紹到這了,更多相關(guān)SpringBoot數(shù)據(jù)脫敏內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot實(shí)現(xiàn)返回值數(shù)據(jù)脫敏的步驟詳解
- Springboot+Jackson自定義注解數(shù)據(jù)脫敏的項(xiàng)目實(shí)踐
- Springboot+Hutool自定義注解實(shí)現(xiàn)數(shù)據(jù)脫敏
- SpringBoot利用自定義注解實(shí)現(xiàn)隱私數(shù)據(jù)脫敏(加密顯示)的解決方案
- SpringBoot利用自定義json序列化器實(shí)現(xiàn)敏感字段數(shù)據(jù)脫敏詳解
- 淺析如何在SpringBoot中實(shí)現(xiàn)數(shù)據(jù)脫敏
- SpringBoot數(shù)據(jù)脫敏的實(shí)現(xiàn)示例
- SpringBoot接口返回?cái)?shù)據(jù)脫敏(Mybatis、Jackson)
相關(guān)文章
java.lang.IllegalArgumentException:Invalid character&nb
本文介紹了java.lang.IllegalArgumentException: Invalid character found異常的解決,方法包括檢查代碼中的方法名,使用合適的HTTP請(qǐng)求方法常量,使用第三方HTTP庫(kù),檢查請(qǐng)求URL以及使用調(diào)試和日志工具,通過(guò)這些方法,我們可以解決異常并確保網(wǎng)絡(luò)應(yīng)用程序的正常運(yùn)行2023-10-10
Mybatis利用OGNL表達(dá)式處理動(dòng)態(tài)sql的方法教程
這篇文章主要給大家介紹了關(guān)于Mybatis利用OGNL表達(dá)式處理動(dòng)態(tài)sql的方法教程的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編一起來(lái)學(xué)習(xí)學(xué)習(xí)吧。2017-06-06
mybatis Reflector反射類(lèi)的具體使用
Reflector類(lèi)是MyBatis反射模塊的核心,負(fù)責(zé)處理類(lèi)的元數(shù)據(jù),以實(shí)現(xiàn)屬性與數(shù)據(jù)庫(kù)字段之間靈活映射的功能,本文主要介紹了mybatis Reflector反射類(lèi)的具體使用,感興趣的可以了解一下2024-02-02
springboot集成mybatis-plus全過(guò)程
本文詳細(xì)介紹了如何在SpringBoot環(huán)境下集成MyBatis-Plus,包括配置maven依賴、application.yaml文件、創(chuàng)建數(shù)據(jù)庫(kù)和Java實(shí)體類(lèi)、Mapper層、Service層和Controller層的設(shè)置,同時(shí),還涵蓋了時(shí)間自動(dòng)填充、分頁(yè)查詢、多對(duì)一和一對(duì)多的數(shù)據(jù)庫(kù)映射關(guān)系設(shè)置2024-09-09
Spring之借助Redis設(shè)計(jì)一個(gè)簡(jiǎn)單訪問(wèn)計(jì)數(shù)器的示例
本篇文章主要介紹了Spring之借助Redis設(shè)計(jì)一個(gè)簡(jiǎn)單訪問(wèn)計(jì)數(shù)器的示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-06-06
由ArrayList來(lái)深入理解Java中的fail-fast機(jī)制
fail-fast俗稱快速失敗,是在多線程進(jìn)行迭代操作時(shí)產(chǎn)生沖突的一種異常拋出機(jī)制,下面我們就由ArrayList來(lái)深入理解Java中的fail-fast機(jī)制.2016-05-05
JVM類(lèi)運(yùn)行機(jī)制實(shí)現(xiàn)原理解析
這篇文章主要介紹了JVM類(lèi)運(yùn)行機(jī)制實(shí)現(xiàn)原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-12-12
Mybatis Plus 字段為空值時(shí)執(zhí)行更新方法未更新解決方案
這篇文章主要介紹了Mybatis Plus 字段為空值時(shí)執(zhí)行更新方法未更新解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09

