java接口冪等性的實(shí)現(xiàn)方式
1. 引言
介紹冪等性的概念
在計(jì)算機(jī)科學(xué)中,冪等性是一種重要的屬性,它指的是一個(gè)操作被執(zhí)行多次和執(zhí)行一次具有相同的效果。換句話說(shuō),無(wú)論這個(gè)操作進(jìn)行多少次,結(jié)果都應(yīng)該是一致的。
這個(gè)概念在多種編程場(chǎng)景中都非常重要,尤其是在分布式系統(tǒng)、網(wǎng)絡(luò)通信和數(shù)據(jù)庫(kù)操作中。
注意:冪等性和防重的本質(zhì)區(qū)別是,防重是多次請(qǐng)求返回報(bào)錯(cuò),而冪等是返回一樣的結(jié)果。
例如,考慮一個(gè)簡(jiǎn)單的HTTP GET請(qǐng)求,它應(yīng)該是冪等的,這意味著無(wú)論你請(qǐng)求多少次,服務(wù)器返回的結(jié)果都應(yīng)該是相同的,不會(huì)因?yàn)槎啻握?qǐng)求而改變服務(wù)器的狀態(tài)。相對(duì)地,一個(gè)POST請(qǐng)求在傳統(tǒng)上不是冪等的,因?yàn)樗赡軙?huì)每次請(qǐng)求都創(chuàng)建一個(gè)新的資源。
為什么需要在Java接口中實(shí)現(xiàn)冪等性
在Java應(yīng)用開發(fā)中,尤其是涉及到網(wǎng)絡(luò)通信和數(shù)據(jù)庫(kù)操作的應(yīng)用,實(shí)現(xiàn)接口的冪等性變得尤為重要。這主要是因?yàn)椋?/p>
- 防止數(shù)據(jù)重復(fù):在網(wǎng)絡(luò)不穩(wěn)定或用戶重復(fù)操作的情況下,確保數(shù)據(jù)不會(huì)被重復(fù)處理,例如,避免因?yàn)橛脩酎c(diǎn)擊了多次“支付”按鈕而多次扣款。
- 提高系統(tǒng)的健壯性:系統(tǒng)能夠處理重復(fù)的請(qǐng)求而不會(huì)出錯(cuò)或產(chǎn)生不一致的結(jié)果,增強(qiáng)了系統(tǒng)對(duì)外界操作的容錯(cuò)能力。
- 簡(jiǎn)化錯(cuò)誤恢復(fù):當(dāng)操作失敗或系統(tǒng)異常時(shí),可以安全地重新執(zhí)行操作,而不需要擔(dān)心會(huì)引起狀態(tài)的錯(cuò)誤或數(shù)據(jù)的不一致。
- 增強(qiáng)用戶體驗(yàn):用戶不需要擔(dān)心多次點(diǎn)擊或操作會(huì)導(dǎo)致不期望的結(jié)果,從而提升用戶的操作體驗(yàn)。
2. 使用冪等表實(shí)現(xiàn)冪等性
實(shí)現(xiàn)流程:
- 在數(shù)據(jù)庫(kù)設(shè)計(jì)階段,加入冪等表。
- 在業(yè)務(wù)邏輯開始前,檢查冪等表中是否已有相應(yīng)的請(qǐng)求記錄。
- 根據(jù)檢查結(jié)果決定是否繼續(xù)處理請(qǐng)求。
- 處理完成后更新冪等表的狀態(tài)。
什么是冪等表
冪等表是一種在數(shù)據(jù)庫(kù)中用于跟蹤已經(jīng)執(zhí)行過(guò)的操作的機(jī)制,以確保即使在多次接收到相同請(qǐng)求的情況下,操作也只會(huì)被執(zhí)行一次。
這種表通常包含足夠的信息來(lái)識(shí)別請(qǐng)求和其執(zhí)行狀態(tài),是實(shí)現(xiàn)接口冪等性的一種有效手段。
如何設(shè)計(jì)冪等表
設(shè)計(jì)冪等表時(shí),關(guān)鍵是確定哪些字段是必需的,以便能夠唯一標(biāo)識(shí)每個(gè)操作。一個(gè)基本的冪等表設(shè)計(jì)可能包括以下字段:
- ID:一個(gè)唯一標(biāo)識(shí)符,通常是主鍵。
- RequestID:請(qǐng)求標(biāo)識(shí)符,用于識(shí)別來(lái)自客戶端的特定請(qǐng)求,這里最好加上唯一鍵索引。
- Status:表示請(qǐng)求處理狀態(tài)(如處理中、成功、失敗)。
- Timestamp:記錄操作的時(shí)間戳。
- Payload(可選):存儲(chǔ)請(qǐng)求的部分或全部數(shù)據(jù),用于后續(xù)處理或?qū)徲?jì)。
示例:Java代碼實(shí)現(xiàn)使用冪等表
以下是一個(gè)簡(jiǎn)單的Java示例,展示如何使用冪等表來(lái)確保接口的冪等性。假設(shè)我們使用Spring框架和JPA來(lái)操作數(shù)據(jù)庫(kù)。
首先,定義一個(gè)冪等性實(shí)體:
import javax.persistence.*; import java.time.LocalDateTime; @Entity @Table(name = "idempotency_control") public class IdempotencyControl { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, unique = true) private String requestId; @Column(nullable = false) private String status; @Column(nullable = false) private LocalDateTime timestamp; // Constructors, getters and setters }
接下來(lái),創(chuàng)建一個(gè)用于操作冪等表的Repository:
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface IdempotencyControlRepository extends JpaRepository<IdempotencyControl, Long> { IdempotencyControl findByRequestId(String requestId); }
最后,實(shí)現(xiàn)一個(gè)服務(wù)來(lái)處理請(qǐng)求,使用冪等表確保操作的冪等性:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class IdempotencyService { @Autowired private IdempotencyControlRepository repository; @Transactional public String processRequest(String requestId, String payload) { IdempotencyControl control = repository.findByRequestId(requestId); if (control != null) { return "Request already processed"; // 通過(guò)control表結(jié)果確定返回的內(nèi)容 } control = new IdempotencyControl(); control.setRequestId(requestId); control.setStatus("PROCESSING"); control.setTimestamp(LocalDateTime.now()); repository.save(control); // Process the request here // Assume processing is successful control.setStatus("COMPLETED"); repository.save(control); return "Request processed successfully"; } }
在這個(gè)示例中,我們首先檢查請(qǐng)求ID是否已存在于數(shù)據(jù)庫(kù)中。如果存在,我們認(rèn)為請(qǐng)求已經(jīng)處理過(guò),直接返回 相應(yīng)信息。
如果不存在,我們將其狀態(tài)標(biāo)記為處理中,處理請(qǐng)求,然后更新狀態(tài)為完成。
這種方法確保了即使在多次接收到相同的請(qǐng)求時(shí),操作的效果也是一致的。
使用冪等表實(shí)現(xiàn)冪等性
關(guān)鍵代碼:
public boolean checkAndInsertIdempotentKey(String requestId) { String sql = "INSERT INTO idempotency_keys (request_id, status, created_at) VALUES (?, 'PENDING', NOW()) ON DUPLICATE KEY UPDATE request_id=request_id"; try { int result = jdbcTemplate.update(sql, requestId); return result == 1; } catch (DuplicateKeyException e) { return false; } }
技術(shù)解析:
- 這段代碼嘗試將一個(gè)新的請(qǐng)求ID插入到冪等表中。如果請(qǐng)求ID已存在,
ON DUPLICATE KEY UPDATE
子句將被觸發(fā),但不會(huì)更改任何記錄,返回的結(jié)果將是0。 - 使用
jdbcTemplate
來(lái)處理數(shù)據(jù)庫(kù)操作,這是Spring框架提供的一個(gè)便利工具,可以簡(jiǎn)化JDBC操作。 - 通過(guò)捕獲
DuplicateKeyException
,我們可以確定請(qǐng)求ID已存在,從而阻止重復(fù)處理。
重要決策和選擇:
- 選擇
ON DUPLICATE KEY UPDATE
是為了確保操作的原子性,避免在檢查鍵是否存在和插入鍵之間進(jìn)行額外的數(shù)據(jù)庫(kù)查詢,這樣可以減少競(jìng)爭(zhēng)條件的風(fēng)險(xiǎn)。
3. 利用Nginx + Lua 和 Redis實(shí)現(xiàn)冪等性
實(shí)現(xiàn)流程:
- 在Nginx服務(wù)器上配置Lua模塊。
- 編寫Lua腳本,利用Redis的SETNX命令檢查和設(shè)置請(qǐng)求標(biāo)志。
- 根據(jù)Lua腳本的執(zhí)行結(jié)果在Nginx層面攔截重復(fù)請(qǐng)求或放行。
Nginx和Lua的作用簡(jiǎn)介
Nginx 是一個(gè)高性能的HTTP和反向代理服務(wù)器,它也常用于負(fù)載均衡。Nginx通過(guò)其輕量級(jí)和高擴(kuò)展性,能夠處理大量的并發(fā)連接,這使得它成為現(xiàn)代高負(fù)載應(yīng)用的理想選擇。
Lua 是一種輕量級(jí)的腳本語(yǔ)言,它可以通過(guò)Nginx的模塊 ngx_lua 嵌入到Nginx中,從而允許開發(fā)者在Nginx配置中直接編寫動(dòng)態(tài)邏輯。這種結(jié)合可以極大地提高Nginx的靈活性和動(dòng)態(tài)處理能力,特別是在處理HTTP請(qǐng)求前的預(yù)處理階段。
介紹Redis的SETNX命令
SETNX 是Redis中的一個(gè)命令,用于“SET if Not eXists”。其基本功能是:只有當(dāng)指定的鍵不存在時(shí),才會(huì)設(shè)置鍵的值。這個(gè)命令常被用于實(shí)現(xiàn)鎖或其他同步機(jī)制,非常適合用來(lái)保證操作的冪等性。
- 如果SETNX成功(即之前鍵不存在),則意味著當(dāng)前操作是第一次執(zhí)行;
- 如果SETNX失?。ㄦI已存在),則意味著操作已經(jīng)被執(zhí)行過(guò)。
架構(gòu)設(shè)計(jì):如何結(jié)合Nginx、Lua和Redis實(shí)現(xiàn)冪等性
在一個(gè)典型的架構(gòu)中,客戶端發(fā)起的請(qǐng)求首先到達(dá)Nginx服務(wù)器。Nginx使用Lua腳本預(yù)處理這些請(qǐng)求,Lua腳本會(huì)檢查Redis中相應(yīng)的鍵是否存在:
- 接收請(qǐng)求:Nginx接收到客戶端的請(qǐng)求。
- Lua腳本處理:Nginx調(diào)用Lua腳本,Lua腳本嘗試在Redis中使用SETNX設(shè)置一個(gè)與請(qǐng)求相關(guān)的唯一鍵。
檢查結(jié)果:
- 如果鍵不存在,Lua腳本設(shè)置鍵并繼續(xù)處理請(qǐng)求(轉(zhuǎn)發(fā)到后端Java應(yīng)用);
- 如果鍵存在,Lua腳本直接返回一個(gè)錯(cuò)誤或提示消息,告知操作已執(zhí)行,防止重復(fù)處理。
示例:配置Nginx和Lua腳本,以及相應(yīng)的Java調(diào)用代碼
Nginx配置部分:
http { lua_shared_dict locks 10m; # 分配10MB內(nèi)存用于存儲(chǔ)鎖信息 server { location /api { default_type 'text/plain'; content_by_lua_block { local redis = require "resty.redis" local red = redis:new() red:set_timeout(1000) -- 1秒超時(shí) local ok, err = red:connect("127.0.0.1", 6379) if not ok then ngx.say("Failed to connect to Redis: ", err) return end local key = "unique_key_" .. ngx.var.request_uri local res, err = red:setnx(key, ngx.var.remote_addr) if res == 0 then ngx.say("Duplicate request") return end -- 設(shè)置鍵的過(guò)期時(shí)間,防止永久占用 red:expire(key, 60) -- 60秒后自動(dòng)刪除鍵 -- 轉(zhuǎn)發(fā)請(qǐng)求到后端應(yīng)用 ngx.exec("@backend") } } location @backend { proxy_pass http://backend_servers; } } }
Java調(diào)用代碼:
Java端不需要特殊處理,因?yàn)閮绲刃缘目刂埔呀?jīng)在Nginx+Lua層面實(shí)現(xiàn)了。Java應(yīng)用只需按照正常邏輯處理從Nginx轉(zhuǎn)發(fā)過(guò)來(lái)的請(qǐng)求即可。
@RestController @RequestMapping("/api") public class ApiController { @PostMapping("/process") public ResponseEntity<String> processRequest(@RequestBody SomeData data) { // 處理請(qǐng)求 return ResponseEntity.ok("Processed successfully"); } }
這種方式將請(qǐng)求的冪等性管理從應(yīng)用層移至更靠前的網(wǎng)絡(luò)層,有助于減輕后端應(yīng)用的負(fù)擔(dān),并提升整體的響應(yīng)速度和系統(tǒng)的可擴(kuò)展性。
利用Nginx + Lua 和 Redis實(shí)現(xiàn)冪等性
關(guān)鍵配置和代碼:
location /api { set_by_lua $token 'return ngx.var.arg_token'; access_by_lua ' local res = ngx.location.capture("/redis", { args = { key = ngx.var.token, value = "EXISTS" } }) if res.body == "EXISTS" then ngx.exit(ngx.HTTP_FORBIDDEN) end '; proxy_pass http://my_backend; }
技術(shù)解析:
- 使用
set_by_lua
從請(qǐng)求中提取token,并在Lua腳本中使用該token。 access_by_lua
塊中,通過(guò)訪問(wèn)內(nèi)部位置/redis
來(lái)查詢Redis中的鍵值。如果鍵已存在,返回403禁止訪問(wèn)狀態(tài)碼,防止進(jìn)一步處理請(qǐng)求。proxy_pass
將請(qǐng)求轉(zhuǎn)發(fā)到后端服務(wù)。
重要決策和選擇:
- 使用Nginx和Lua的組合允許在請(qǐng)求達(dá)到應(yīng)用服務(wù)器之前進(jìn)行預(yù)處理,減輕后端的負(fù)擔(dān)。
- 通過(guò)Redis進(jìn)行快速鍵值檢查,利用其性能優(yōu)勢(shì)確保操作的速度和效率。
4. 利用AOP實(shí)現(xiàn)冪等性
實(shí)現(xiàn)流程:
- 定義一個(gè)切面,專門處理冪等性邏輯。
- 在適當(dāng)?shù)那腥朦c(diǎn)(如服務(wù)層方法)使用前置通知進(jìn)行冪等檢查。
- 根據(jù)業(yè)務(wù)需求,可能還需要在方法執(zhí)行后通過(guò)后置通知更新狀態(tài)。
介紹AOP(面向切面編程)的基本概念
面向切面編程(AOP) 是一種編程范式,旨在通過(guò)將應(yīng)用程序邏輯從系統(tǒng)服務(wù)中分離出來(lái)來(lái)增強(qiáng)模塊化。這種方法主要用于處理橫切關(guān)注點(diǎn),如日志記錄、事務(wù)管理、數(shù)據(jù)驗(yàn)證等,這些通常會(huì)分散在多個(gè)模塊或組件中。AOP通過(guò)定義切面(aspects),使得這些關(guān)注點(diǎn)的實(shí)現(xiàn)可以集中管理和復(fù)用。
在Java中,Spring框架通過(guò)Spring AOP提供了面向切面編程的支持,允許開發(fā)者通過(guò)簡(jiǎn)單的注解或XML配置來(lái)定義切面、切點(diǎn)(pointcuts)和通知(advices)。
使用Spring AOP實(shí)現(xiàn)冪等性的策略
在實(shí)現(xiàn)接口冪等性的上下文中,可以使用Spring AOP來(lái)攔截接口調(diào)用,并進(jìn)行必要的冪等檢查。這通常涉及以下步驟:
- 定義切點(diǎn):指定哪些方法需要冪等性保護(hù)。
- 前置通知:在方法執(zhí)行前,檢查某個(gè)標(biāo)識(shí)符(如請(qǐng)求ID)是否已存在于Redis中,如果存在,則阻止方法執(zhí)行。
- 后置通知:在方法執(zhí)行后,將請(qǐng)求ID添加到Redis中,以標(biāo)記此操作已完成。
示例:定義切面,編寫After通知更新Redis狀態(tài)
以下是一個(gè)使用Spring AOP來(lái)實(shí)現(xiàn)冪等性的示例,包括定義切面和編寫后置通知來(lái)更新Redis狀態(tài)。
定義切面:
首先,需要定義一個(gè)切面和一個(gè)切點(diǎn),這個(gè)切點(diǎn)匹配所有需要冪等性保護(hù)的方法:
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.annotation.AfterReturning; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.data.redis.core.StringRedisTemplate; @Aspect @Component public class IdempotenceAspect { @Autowired private StringRedisTemplate redisTemplate; @Pointcut("@annotation(Idempotent)") // 假設(shè)Idempotent是一個(gè)自定義注解,用于標(biāo)記需要冪等保護(hù)的方法 public void idempotentOperation() {} @AfterReturning("idempotentOperation()") public void afterReturning(JoinPoint joinPoint) { // 獲取請(qǐng)求標(biāo)識(shí) String key = extractKeyFromJoinPoint(joinPoint); // 將操作標(biāo)識(shí)存入Redis中,標(biāo)記為已處理 redisTemplate.opsForValue().set(key, "processed", 10, TimeUnit.MINUTES); // 示例中設(shè)置10分鐘后過(guò)期 } private String extractKeyFromJoinPoint(JoinPoint joinPoint) { // 此處實(shí)現(xiàn)從方法參數(shù)等獲取key的邏輯 return "SOME_KEY"; } }
在這個(gè)例子中,Idempotent
注解用于標(biāo)記那些需要冪等性保護(hù)的方法。@AfterReturning
通知確保只有在方法成功執(zhí)行后,請(qǐng)求標(biāo)識(shí)才會(huì)被添加到Redis中。這樣可以防止在執(zhí)行過(guò)程中發(fā)生異常時(shí)錯(cuò)誤地標(biāo)記請(qǐng)求為已處理。
這種方法的優(yōu)點(diǎn)是它將冪等性邏輯與業(yè)務(wù)代碼解耦,使得業(yè)務(wù)邏輯更加清晰,同時(shí)集中管理冪等性保護(hù)。
利用AOP實(shí)現(xiàn)冪等性
關(guān)鍵代碼:
@Aspect @Component public class IdempotencyAspect { @Autowired private RedisTemplate<String, String> redisTemplate; @AfterReturning(pointcut = "execution(* com.example.service.*.*(..)) && @annotation(Idempotent)", returning = "result") public void afterReturningAdvice(JoinPoint joinPoint, Object result) { String key = getKeyFromJoinPoint(joinPoint); redisTemplate.opsForValue().set(key, "COMPLETED", 10, TimeUnit.MINUTES); } private String getKeyFromJoinPoint(JoinPoint joinPoint) { // Logic to extract key based on method arguments or annotations } }
技術(shù)解析:
- 定義了一個(gè)切面
IdempotencyAspect
,它在帶有@Idempotent
注解的方法執(zhí)行成功后運(yùn)行。 - 使用
@AfterReturning
通知來(lái)更新Redis中的鍵狀態(tài),標(biāo)記為“COMPLETED”。
重要決策和選擇:
- 選擇AOP允許開發(fā)者不侵入業(yè)務(wù)代碼地實(shí)現(xiàn)冪等性,提高代碼的可維護(hù)性和清晰性。
- 使用Redis來(lái)存儲(chǔ)操作狀態(tài),利用其快速訪問(wèn)和過(guò)期機(jī)制來(lái)自動(dòng)管理狀態(tài)數(shù)據(jù)。
這些解析和決策展示了如何在不同層面上通過(guò)技術(shù)手段確保Java接口的冪等性,每種方法都有其適用場(chǎng)景和優(yōu)勢(shì)。
5. 實(shí)戰(zhàn)應(yīng)用和測(cè)試
提供測(cè)試示例和結(jié)果
測(cè)試冪等表:
- 場(chǎng)景:模擬用戶重復(fù)提交訂單請(qǐng)求。
- 操作:連續(xù)發(fā)送相同的訂單創(chuàng)建請(qǐng)求。
- 預(yù)期結(jié)果:第一次請(qǐng)求創(chuàng)建訂單成功,后續(xù)請(qǐng)求被攔截,返回提示信息如“操作已處理”。
測(cè)試代碼示例:
// 假設(shè)有一個(gè)訂單提交的接口 @PostMapping("/submitOrder") public ResponseEntity<String> submitOrder(@RequestBody Order order) { boolean isProcessed = idempotencyService.checkAndRecord(order.getId()); if (!isProcessed) { return ResponseEntity.ok("訂單已成功提交"); } else { return ResponseEntity.status(HttpStatus.CONFLICT).body("操作已處理"); } }
測(cè)試Nginx + Lua + Redis
- 場(chǎng)景:用戶在短時(shí)間內(nèi)多次點(diǎn)擊支付按鈕。
- 操作:模擬快速連續(xù)發(fā)送支付請(qǐng)求。
- 預(yù)期結(jié)果:第一次請(qǐng)求處理支付,后續(xù)請(qǐng)求在Nginx層面被攔截,返回錯(cuò)誤或提示信息。
測(cè)試Spring AOP
- 場(chǎng)景:調(diào)用API接口進(jìn)行資源創(chuàng)建。
- 操作:連續(xù)調(diào)用同一API接口。
- 預(yù)期結(jié)果:通過(guò)AOP切面的前置通知,第一次調(diào)用執(zhí)行資源創(chuàng)建,后續(xù)調(diào)用返回已處理的狀態(tài)。
測(cè)試代碼示例:
// AOP切面處理 @Aspect @Component public class IdempotencyAspect { @Autowired private IdempotencyService idempotencyService; @Before("@annotation(Idempotent) && args(request,..)") public void checkIdempotency(JoinPoint joinPoint, IdempotentRequest request) throws Throwable { if (!idempotencyService.isRequestUnique(request.getRequestId())) { throw new IdempotencyException("Duplicate request detected."); } } }
測(cè)試結(jié)果 應(yīng)該顯示冪等性邏輯有效阻止了重復(fù)操作,從而確保了系統(tǒng)的穩(wěn)定性和數(shù)據(jù)的一致性。這些測(cè)試不僅驗(yàn)證了功能的正確性,還可以在系統(tǒng)壓力測(cè)試中評(píng)估冪等性解決方案的性能影響。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
解讀@RequestBody與post請(qǐng)求的關(guān)系
這篇文章主要介紹了解讀@RequestBody與post請(qǐng)求的關(guān)系,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12Mybatis使用foreach批量更新數(shù)據(jù)報(bào)無(wú)效字符錯(cuò)誤問(wèn)題
這篇文章主要介紹了Mybatis使用foreach批量更新數(shù)據(jù)報(bào)無(wú)效字符錯(cuò)誤問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08Java靜態(tài)方法和實(shí)例方法區(qū)別詳解
這篇文章主要為大家詳細(xì)介紹了Java靜態(tài)方法和實(shí)例方法的區(qū)別,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12SpringBoot定義Bean的幾種實(shí)現(xiàn)方式
本文主要介紹了SpringBoot定義Bean的幾種實(shí)現(xiàn)方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05JDK17、JDK19、JDK1.8輕松切換(無(wú)坑版,小白也可以看懂!)
在做不同的java項(xiàng)目時(shí)候,因項(xiàng)目需要很可能來(lái)回切換jdk版本,下面這篇文章主要介紹了JDK17、JDK19、JDK1.8輕松切換的相關(guān)資料,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02Java中ReUtil正則表達(dá)式工具庫(kù)的使用
ReUtil是Hutool庫(kù)中的正則表達(dá)式工具類,提供了多種常用正則表達(dá)式操作方法,下面就來(lái)介紹一下ReUtil的使用,具有一定的參考價(jià)值,感興趣的可以了解一下2025-02-02詳解Java如何實(shí)現(xiàn)一個(gè)優(yōu)秀的散列表
這篇文章主要通過(guò)簡(jiǎn)單的示例為大家詳細(xì)介紹了在Java中如何實(shí)現(xiàn)一個(gè)優(yōu)秀的散列表,文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,需要的可以了解一下2023-07-07SpringBoot集成Dubbo啟用gRPC協(xié)議
這篇文章主要介紹了SpringBoot集成Dubbo啟用gRPC協(xié)議,以及與原生 gRPC 在代碼編寫過(guò)程中的區(qū)別。感興趣的同學(xué)可以參考閱讀2023-04-04