欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java冪等性校驗(yàn)解決重復(fù)點(diǎn)擊的六種實(shí)現(xiàn)方式

 更新時(shí)間:2025年04月23日 11:22:25   作者:不愿放下技術(shù)的小趙  
在分布式系統(tǒng)中,冪等性(Idempotency)是一個(gè)非常重要的概念,,冪等性操作指的是:無(wú)論這個(gè)操作執(zhí)行多少次,結(jié)果都應(yīng)該是相同的,這是為了避免重復(fù)執(zhí)行操作引起數(shù)據(jù)的不一致,本文給大家介紹了Java冪等性校驗(yàn)解決重復(fù)點(diǎn)擊的六種實(shí)現(xiàn)方式,需要的朋友可以參考下

一、簡(jiǎn)介

1.1 什么是冪等?

冪等 是一個(gè)數(shù)學(xué)與計(jì)算機(jī)科學(xué)概念,英文 idempotent [a??demp?t?nt]。

  • 在數(shù)學(xué)中,冪等用函數(shù)表達(dá)式就是:f(x) = f(f(x))。比如 求絕對(duì)值 的函數(shù),就是冪等的,abs(x) = abs(abs(x))。
  • 計(jì)算機(jī)科學(xué)中,冪等表示一次和多次請(qǐng)求某一個(gè)資源應(yīng)該具有同樣的作用。

滿足冪等條件的性能叫做 冪等性。

1.2 為什么需要冪等性?

我們開(kāi)發(fā)一個(gè)轉(zhuǎn)賬功能,假設(shè)我們調(diào)用下游接口 超時(shí) 了。一般情況下,超時(shí)可能是網(wǎng)絡(luò)傳輸丟包的問(wèn)題,也可能是請(qǐng)求時(shí)沒(méi)送到,還有可能是請(qǐng)求到了,返回結(jié)果卻丟了。這時(shí)候我們是否可以 重試 呢?如果重試的話,是否會(huì)多賺了一筆錢呢?

在這里插入圖片描述

在我們?nèi)粘i_(kāi)發(fā)中,會(huì)存在各種不同系統(tǒng)之間的相互遠(yuǎn)程調(diào)用。調(diào)用遠(yuǎn)程服務(wù)會(huì)有三個(gè)狀態(tài):成功、失敗、超時(shí)。

前兩者都是明確的狀態(tài),但超時(shí)則是 未知狀態(tài)。我們轉(zhuǎn)賬 超時(shí) 的時(shí)候,如果下游轉(zhuǎn)賬系統(tǒng)做好 冪等性校驗(yàn),我們判斷超時(shí)后直接發(fā)起重試,既可以保證轉(zhuǎn)賬正常進(jìn)行,又可以保證不會(huì)多轉(zhuǎn)一筆。

日常開(kāi)發(fā)中,需要考慮冪等性的場(chǎng)景:

  • 前端重復(fù)提交:比如提交 form 表單時(shí),如果快速點(diǎn)擊提交按鈕,就可能產(chǎn)生兩條一樣的數(shù)據(jù)。
  • 用戶惡意刷 單:例如在用戶投票這種功能時(shí),如果用戶針對(duì)一個(gè)用戶進(jìn)行重復(fù)提交投票,這樣會(huì)導(dǎo)致接口接收到用戶重復(fù)提交的投票信息,會(huì)使投票結(jié)果與事實(shí)嚴(yán)重不符。
  • 接口超時(shí)重復(fù)提交:很多時(shí)候 HTTP 客戶端工具都默認(rèn)開(kāi)啟超時(shí)重試的機(jī)制,尤其是第三方調(diào)用接口的時(shí)候,為了防止網(wǎng)絡(luò)波動(dòng)等造成的請(qǐng)求失敗,都會(huì)添加重試機(jī)制,導(dǎo)致一個(gè)請(qǐng)求提交多次。
  • MQ重復(fù)消費(fèi):消費(fèi)者讀取消息時(shí),有可能會(huì)讀取到重復(fù)消息。

1.3 接口超時(shí),應(yīng)該如何處理?

如果我們調(diào)用下游接口超時(shí)了,我們應(yīng)該如何處理?其實(shí)從生產(chǎn)者和消費(fèi)者兩個(gè)角度來(lái)看,有兩種方案處理:

  • 方案一:消費(fèi)者角度。在接口超時(shí)后,調(diào)用下游接口檢查數(shù)據(jù)狀態(tài)
    • 如果查詢到是成功,就走成功流程;
    • 如果是失敗,就按失敗處理(重新請(qǐng)求)。

在這里插入圖片描述

  • 方案二:生產(chǎn)者角度。下游接口支持冪等,上有系統(tǒng)如果調(diào)用超時(shí),發(fā)起重試即可。

在這里插入圖片描述

兩種方案都是可以的,但如果是 MQ重復(fù)消費(fèi)的場(chǎng)景,方案一處理并不是很妥當(dāng),所以我們還是要求下游系統(tǒng) 對(duì)外接口支持冪等

1.4 冪等性對(duì)系統(tǒng)的影響

冪等性是為了簡(jiǎn)化客戶端邏輯處理,能防止重復(fù)提交等操作,但卻增加了 服務(wù)端的邏輯復(fù)雜性和成本,其主要是:

  • 把并行執(zhí)行的功能改為串行執(zhí)行,降低了執(zhí)行效率。
  • 增加了額外控制冪等的業(yè)務(wù)邏輯,復(fù)雜化了業(yè)務(wù)功能。

在使用前,需要根據(jù)實(shí)際業(yè)務(wù)場(chǎng)景具體分析,除了業(yè)務(wù)上的特殊要求外,一般情況下不需要引入接口的冪等性。

二、Restful API 接口的冪等性

Restful 推薦的幾種 HTTP 接口方法中,不同的請(qǐng)求對(duì)冪等性的要求不同:

請(qǐng)求類型是否冪等描述
GETGET 方法用于獲取資源。一般不會(huì)也不應(yīng)當(dāng)對(duì)系統(tǒng)資源進(jìn)行改變,所以是冪等的。
POSTPOST 方法用于創(chuàng)建新的資源。每次執(zhí)行都會(huì)新增數(shù)據(jù),所以不是冪等的。
PUT不一定PUT 方法一般用于修改資源。該操作分情況判斷是否滿足冪等,更新中直接根據(jù)某個(gè)值進(jìn)行更新,也能保持冪等。不過(guò)執(zhí)行累加操作的更新是非冪等的。
DELETE不一定DELETE 方法一般用于刪除資源。該操作分情況判斷是否滿足冪等,當(dāng)根據(jù)唯一值進(jìn)行刪除時(shí),滿足冪等;但是帶查詢條件的刪除則不一定滿足。例如:根據(jù)條件刪除一批數(shù)據(jù)后,又有新增數(shù)據(jù)滿足該條件,再執(zhí)行就會(huì)將新增數(shù)據(jù)刪除,需要根據(jù)業(yè)務(wù)判斷是否校驗(yàn)冪等。

三、實(shí)現(xiàn)方式

3.1 數(shù)據(jù)庫(kù)層面,主鍵/唯一索引沖突

日常開(kāi)發(fā)中,為了實(shí)現(xiàn)接口冪等性校驗(yàn),可以這樣實(shí)現(xiàn):

  • 提前在數(shù)據(jù)庫(kù)中為唯一存在的字段(如:唯一流水號(hào) bizSeq 字段)添加唯一索引,或者直接設(shè)置為主鍵。
  • 請(qǐng)求過(guò)來(lái),直接將數(shù)據(jù)插入、更新到數(shù)據(jù)庫(kù)中,并進(jìn)行 try-catch 捕獲。
  • 如果拋出異常,說(shuō)明為重復(fù)請(qǐng)求,可以直接返回成功,或提示請(qǐng)求重復(fù)。

補(bǔ)充: 也可以新建一張 防止重復(fù)點(diǎn)擊表,將唯一標(biāo)識(shí)放到表中,存為主鍵或唯一索引,然后配合 tra-catch 對(duì)重復(fù)點(diǎn)擊的請(qǐng)求進(jìn)行處理。

偽代碼如下:

/**
 * 冪等處理
 */
Rsp idempotent(Request req){
  
    try {
        insert(req);
    } catch (DuplicateKeyException e) {
        //攔截是重復(fù)請(qǐng)求,直接返回成功
        log.info("主鍵沖突,是重復(fù)請(qǐng)求,直接返回成功,流水號(hào):{}",bizSeq);
        return rsp;
    }

    //正常處理請(qǐng)求
    dealRequest(req);

    return rsp;
}

3.2 數(shù)據(jù)庫(kù)層面,樂(lè)觀鎖

樂(lè)觀鎖:樂(lè)觀鎖在操作數(shù)據(jù)時(shí),非常樂(lè)觀,認(rèn)為別人不會(huì)同時(shí)在修改數(shù)據(jù)。因此樂(lè)觀鎖不會(huì)上鎖,只是在執(zhí)行更新的時(shí)候判斷一下,在此期間是否有人修改了數(shù)據(jù)。

樂(lè)觀鎖的實(shí)現(xiàn):

就是給表多加一列 version 版本號(hào),每次更新數(shù)據(jù)前,先查出來(lái)確認(rèn)下是不是剛剛的版本號(hào),沒(méi)有改動(dòng)再去執(zhí)行更新,并升級(jí) version(version=version+1)。

比如,我們更新前,先查一下數(shù)據(jù),查出來(lái)的版本號(hào)是 version=1。

select order_id,version from order where order_id='666';

然后使用 version=1 和 訂單ID 一起作為條件,再去更新:

update order set version = version +1,status='P' where order_id='666' and version =1

最后,更新成功才可以處理業(yè)務(wù)邏輯,如果更新失敗,默認(rèn)為重復(fù)請(qǐng)求,直接返回。

流程圖如下:

為什么版本號(hào)建議自增呢?

因?yàn)闃?lè)觀鎖存在 ABA 的問(wèn)題,如果 version 版本一直是自增的就不會(huì)出現(xiàn) ABA 的情況。

3.3 數(shù)據(jù)庫(kù)層面,悲觀鎖(select for update)【不推薦】

悲觀鎖:通俗點(diǎn)講就是很悲觀,每次去操作數(shù)據(jù)時(shí),都覺(jué)得別人中途會(huì)修改,所以每次在拿數(shù)據(jù)的時(shí)候都會(huì)上鎖。官方點(diǎn)講就是,共享資源每次只給一個(gè)線程使用,其他線程阻塞,用完后再把資源轉(zhuǎn)讓給其它資源。

悲觀鎖的實(shí)現(xiàn):

在訂單業(yè)務(wù)場(chǎng)景中,假設(shè)先查詢出訂單,如果查到的是處理中狀態(tài),就處理完業(yè)務(wù),然后再更新訂單狀態(tài)為完成。如果查到訂單,并且不是處理中的狀態(tài),則直接返回。

可以使用數(shù)據(jù)庫(kù)悲觀鎖(select … for update)解決這個(gè)問(wèn)題:

begin;  # 1.開(kāi)始事務(wù)
select * from order where order_id='666' for update # 查詢訂單,判斷狀態(tài),鎖住這條記錄
if(status !=處理中){
   //非處理中狀態(tài),直接返回;
   return ;
}
## 處理業(yè)務(wù)邏輯
update order set status='完成' where order_id='666' # 更新完成
commit; # 5.提交事務(wù)

注意:

  • 這里的 order_id 需要是主鍵或索引,只用行級(jí)鎖鎖住這條數(shù)據(jù)即可,如果不是主鍵或索引,會(huì)鎖住整張表。
  • 悲觀鎖在同一事務(wù)操作過(guò)程中,鎖住了一行數(shù)據(jù)。這樣 別的請(qǐng)求過(guò)來(lái)只能等待,如果當(dāng)前事務(wù)耗時(shí)比較長(zhǎng),就很影響接口性能。所以一般 不建議用悲觀鎖的實(shí)現(xiàn)方式。

3.4 數(shù)據(jù)庫(kù)層面,狀態(tài)機(jī)

很多業(yè)務(wù)表,都是由狀態(tài)的,比如:轉(zhuǎn)賬流水表,就會(huì)有 0-待處理,1-處理中,2-成功,3-失敗的狀態(tài)。轉(zhuǎn)賬流水更新的時(shí)候,都會(huì)涉及流水狀態(tài)更新,即涉及 狀態(tài)機(jī)(即狀態(tài)變更圖)。我們可以利用狀態(tài)機(jī)來(lái)實(shí)現(xiàn)冪等性校驗(yàn)。

狀態(tài)機(jī)的實(shí)現(xiàn):

比如:轉(zhuǎn)賬成功后,把 處理中 的轉(zhuǎn)賬流水更新為成功的狀態(tài),SQL 如下:

update transfor_flow set status = 2 where biz_seq='666' and status = 1;

流程圖如下:

在這里插入圖片描述

  • 第1次請(qǐng)求來(lái)時(shí),bizSeq 流水號(hào)是 666,該流水的狀態(tài)是處理中,值是 1,要更新為 2-成功的狀態(tài),所以該 update 語(yǔ)句可以正常更新數(shù)據(jù),sql 執(zhí)行結(jié)果的影響行數(shù)是 1,流水狀態(tài)最后變成了 2。
  • 第2次請(qǐng)求也過(guò)來(lái)了,如果它的流水號(hào)還是 666,因?yàn)樵摿魉疇顟B(tài)已經(jīng)變?yōu)?2-成功的狀態(tài),所以更新結(jié)果是0,不會(huì)再處理業(yè)務(wù)邏輯,接口直接返回。

偽代碼實(shí)現(xiàn)如下:

Rsp idempotentTransfer(Request req){
    String bizSeq = req.getBizSeq();
    int rows= "update transfr_flow set status=2 where biz_seq=#{bizSeq} and status=1;"
    if(rows==1){
        log.info(“更新成功,可以處理該請(qǐng)求”);
        //其他業(yè)務(wù)邏輯處理
        return rsp;
    } else if(rows == 0) {
        log.info(“更新不成功,不處理該請(qǐng)求”);
        //不處理,直接返回
        return rsp;
    }

    log.warn("數(shù)據(jù)異常")
    return rsp:
}

3.5 應(yīng)用層面,token令牌【不推薦】

token 唯一令牌方案一般包括兩個(gè)請(qǐng)求階段:

  • 客戶端請(qǐng)求申請(qǐng)獲取請(qǐng)求接口用的token,服務(wù)端生成token返回;
  • 客戶端帶著token請(qǐng)求,服務(wù)端校驗(yàn)token。

流程圖如下:

在這里插入圖片描述

  1. 客戶端發(fā)送請(qǐng)求,申請(qǐng)獲取 token。
  2. 服務(wù)端生成全局唯一的 token,保存到 redis 中(一般會(huì)設(shè)置一個(gè)過(guò)期時(shí)間),然后返回給客戶端。
  3. 客戶端帶著 token,發(fā)起請(qǐng)求。
  4. 服務(wù)端去 redis 確認(rèn) token 是否存在,一般用 redis.del(token) 的方式,如果存在會(huì)刪除成功,即處理業(yè)務(wù)邏輯,如果刪除失敗,則直接返回結(jié)果。

補(bǔ)充: 這種方式個(gè)人不推薦,說(shuō)兩方面原因:

  • 需要前后端聯(lián)調(diào)才能實(shí)現(xiàn),存在溝通成本,最終效果可能與設(shè)想不一致。
  • 如果前端多次獲取多個(gè) token,還是可以重復(fù)請(qǐng)求的,如果再在獲取 token 處加分布式鎖控制,就不如直接用分布式鎖來(lái)控制冪等性了,即下面這種解決方式。

3.6 應(yīng)用層面,分布式鎖【推薦】

分布式鎖 實(shí)現(xiàn)冪等性的邏輯就是,請(qǐng)求過(guò)來(lái)時(shí),先去嘗試獲取分布式鎖,如果獲取成功,就執(zhí)行業(yè)務(wù)邏輯,反之獲取失敗的話,就舍棄請(qǐng)求直接返回成功。

流程圖如下:

  • 分布式鎖可以使用 Redis,也可以使用 Zookeeper,不過(guò) Redis 相對(duì)好點(diǎn),比較輕量級(jí)。
  • Redis 分布式鎖,可以使用 setIfAbsent() 來(lái)實(shí)現(xiàn),注意分布式鎖的 key 必須為業(yè)務(wù)的唯一標(biāo)識(shí)。
  • Redis 執(zhí)行設(shè)置 key 的動(dòng)作時(shí),要設(shè)置過(guò)期時(shí)間,防止釋放鎖失敗。這個(gè)過(guò)期時(shí)間不能太短,太短攔截不了重復(fù)請(qǐng)求,也不能設(shè)置太長(zhǎng),請(qǐng)求量多的話會(huì)占用存儲(chǔ)空間。

四、Java 代碼實(shí)現(xiàn)

4.1 @NotRepeat 注解

@NotRepeat 注解用于修飾需要進(jìn)行冪等性校驗(yàn)的類。

NotRepeat.java

import java.lang.annotation.*;

/**
 * 冪等性校驗(yàn)注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NotRepeat {

}

4.2 AOP 切面

AOP切面監(jiān)控被 @Idempotent 注解修飾的方法調(diào)用,實(shí)現(xiàn)冪等性校驗(yàn)邏輯。

IdempotentAOP.java

import com.demo.util.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;

/**
 * 重復(fù)點(diǎn)擊校驗(yàn)
 */
@Slf4j
@Aspect
@Component
public class IdempotentAOP {
    
    /** Redis前綴 */
    private String API_IDEMPOTENT_CHECK = "API_IDEMPOTENT_CHECK:";

    @Resource
    private HttpServletRequest request;
    @Resource
    private RedisUtils redisUtils;

    /**
     * 定義切面
     */
    @Pointcut("@annotation(com.demo.annotation.NotRepeat)")
    public void notRepeat() {
    }

    /**
     * 在接口原有的方法執(zhí)行前,將會(huì)首先執(zhí)行此處的代碼
     */
    @Before("notRepeat()")
    public void doBefore(JoinPoint joinPoint) {
        String uri = request.getRequestURI();

        // 登錄后才做校驗(yàn)
        UserInfo loginUser = AuthUtil.getLoginUser();
        if (loginUser != null) {
            assert uri != null;
            String key = loginUser.getAccount() + "_" + uri;
            log.info(">>>>>>>>>> 【IDEMPOTENT】開(kāi)始冪等性校驗(yàn),加鎖,account: {},uri: {}", loginUser.getAccount(), uri);

            // 加分布式鎖
            boolean lockSuccess = redisUtils.setIfAbsent(API_IDEMPOTENT_CHECK + key, "1", 30, TimeUnit.MINUTES);
            log.info(">>>>>>>>>> 【IDEMPOTENT】分布式鎖是否加鎖成功:{}", lockSuccess);
            if (!lockSuccess) {
                if (uri.contains("contract/saveDraftContract")) {
                    log.error(">>>>>>>>>> 【IDEMPOTENT】文件保存中,請(qǐng)稍后");
                    throw new IllegalArgumentException("文件保存中,請(qǐng)稍后");

                } else if (uri.contains("contract/saveContract")) {
                    log.error(">>>>>>>>>> 【IDEMPOTENT】文件發(fā)起中,請(qǐng)稍后");
                    throw new IllegalArgumentException("文件發(fā)起中,請(qǐng)稍后");
                }
            }
        }
    }

    /**
     * 在接口原有的方法執(zhí)行后,都會(huì)執(zhí)行此處的代碼(final)
     */
    @After("notRepeat()")
    public void doAfter(JoinPoint joinPoint) {
        // 釋放鎖
        String uri = request.getRequestURI();
        assert uri != null;
        UserInfo loginUser = SysUserUtil.getloginUser();
        if (loginUser != null) {
            String key = loginUser.getAccount() + "_" + uri;
            log.info(">>>>>>>>>> 【IDEMPOTENT】?jī)绲刃孕r?yàn)結(jié)束,釋放鎖,account: {},uri: {}", loginUser.getAccount(), uri);
            redisUtils.del(API_IDEMPOTENT_CHECK + key);
        }
    }
}

4.3 RedisUtils 工具類

RedisUtils.java

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;


/**
 * redis工具類
 */
@Slf4j
@Component
public class RedisUtils {

    /**
     * 默認(rèn)RedisObjectSerializer序列化
     */
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 加分布式鎖
     */
    public boolean setIfAbsent(String key, String value, long timeout, TimeUnit unit) {
        return redisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit);
    }

    /**
     * 釋放鎖
     */
    public void del(String... keys) {
        if (keys != null && keys.length > 0) {
            //將參數(shù)key轉(zhuǎn)為集合
            redisTemplate.delete(Arrays.asList(keys));
        }
    }
}

4.4 測(cè)試類

OrderController.java

import com.demo.annotation.NotRepeat;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;
import java.util.List;

/**
 * 冪等性校驗(yàn)測(cè)試類
 */
@RequestMapping("/order")
@RestController
public class OrderController {

    @NotRepeat
    @GetMapping("/orderList")
    public List<String> orderList() {
        // 查詢列表
        return Arrays.asList("Order_A", "Order_B", "Order_C");
        // throw new RuntimeException("參數(shù)錯(cuò)誤");
    }
}

4.5 測(cè)試結(jié)果

請(qǐng)求地址:http://localhost:8080/order/orderList

日志信息如下:

在這里插入圖片描述

經(jīng)測(cè)試,加鎖后,正常處理業(yè)務(wù)、拋出異常都可以正常釋放鎖。

以上就是Java冪等性校驗(yàn)解決重復(fù)點(diǎn)擊的六種實(shí)現(xiàn)方式的詳細(xì)內(nèi)容,更多關(guān)于Java冪等性解決重復(fù)點(diǎn)擊的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • springboot FeignClient注解及參數(shù)

    springboot FeignClient注解及參數(shù)

    這篇文章主要介紹了springboot FeignClient注解及參數(shù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • Java 使用Thumbnails對(duì)大圖片壓縮

    Java 使用Thumbnails對(duì)大圖片壓縮

    這篇文章主要介紹了Java 使用Thumbnails對(duì)大圖片壓縮,幫助大家更好的利用Java處理圖片,感興趣的朋友可以了解下
    2020-11-11
  • Spring IOC簡(jiǎn)單理解及創(chuàng)建對(duì)象的方式

    Spring IOC簡(jiǎn)單理解及創(chuàng)建對(duì)象的方式

    這篇文章主要介紹了Spring IOC簡(jiǎn)單理解及創(chuàng)建對(duì)象的方式,本文通過(guò)兩種方式給大家介紹創(chuàng)建對(duì)象的方法,通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2021-09-09
  • Java獲取某一日期的前N天(使用Calendar類)

    Java獲取某一日期的前N天(使用Calendar類)

    這篇文章主要給大家介紹了關(guān)于Java如何使用Calendar類獲取某一日期的前N天,我們可以使用Java中的Calendar類來(lái)獲取前n天的時(shí)間,文中給出了詳細(xì)的代碼示例,需要的朋友可以參考下
    2023-08-08
  • SpringBoot2使用JTA組件實(shí)現(xiàn)基于JdbcTemplate多數(shù)據(jù)源事務(wù)管理(親測(cè)好用)

    SpringBoot2使用JTA組件實(shí)現(xiàn)基于JdbcTemplate多數(shù)據(jù)源事務(wù)管理(親測(cè)好用)

    這篇文章主要介紹了SpringBoot2使用JTA組件實(shí)現(xiàn)基于JdbcTemplate多數(shù)據(jù)源事務(wù)管理(親測(cè)好用),在Spring?Boot?2.x中,整合了這兩個(gè)JTA的實(shí)現(xiàn)分別是Atomikos和Bitronix,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-07-07
  • java優(yōu)先隊(duì)列PriorityQueue中Comparator的用法詳解

    java優(yōu)先隊(duì)列PriorityQueue中Comparator的用法詳解

    這篇文章主要介紹了java優(yōu)先隊(duì)列PriorityQueue中Comparator的用法詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • 詳解java迭代器模式

    詳解java迭代器模式

    這篇文章主要介紹了java迭代器模式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-05-05
  • Java序列化反序列化原理及漏洞解決方案

    Java序列化反序列化原理及漏洞解決方案

    這篇文章主要介紹了Java序列化反序列化原理及漏洞解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • Java類中字段可以不賦予初始值的原因分析

    Java類中字段可以不賦予初始值的原因分析

    這篇文章主要介紹了Java類中字段可以不賦予初始值的原因分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • springboot配置文件讀取pom文件信息方式

    springboot配置文件讀取pom文件信息方式

    這篇文章主要介紹了springboot配置文件讀取pom文件信息方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02

最新評(píng)論