Java如何提供給第三方使用接口方法詳解
前言
相信有很多小伙伴,在日常的開發(fā)中都有遇到過需要調(diào)用第三方接口的需求吧,但是自己有沒有寫過接口提供給第三方使用呢,常規(guī)的都是我們調(diào)用別人的接口,但是自己需要開發(fā)接口提供給第三方使用的場景應(yīng)該不是很多,很多小伙伴可能會(huì)想不就開發(fā)一個(gè)接口對外開放嘛豈不是很簡單,但是在開發(fā)接口對外開放,我們需要考慮一個(gè)問題,沒有限制條件,那豈不是太不安全了,誰都可以調(diào)我這個(gè)接口了啊。
所以接下來的就是我們需要考慮的問題了,在開發(fā)接口的時(shí)候就要考慮到安全性的問題,那么應(yīng)該如何去解決這個(gè)問題呢?提供接口給第三方使用的時(shí)候需要加上校驗(yàn)保證接口的安全性。
下面是我寫的一個(gè)例子希望對大家有幫助。
接口Controller
在寫接口前一定要簽名做簽名校驗(yàn),我的簽名方式做了特殊處理,因?yàn)榻涌谑菍ν忾_放的,這個(gè)是為了避免惡意調(diào)用接口做的處理,叫做簽名的混淆值,這個(gè)簽名混淆值的作用是就算別人知道了接口,并且知道簽名方式也不能被攻擊,是為了避免被惡意篡改數(shù)據(jù),簽名混淆值就是一組特定加密后的數(shù)據(jù)。
@PostMapping("refundDeductionPoints")
public Result<SysIntegralStatement> refundDeductionPoints (@RequestParam Map<String,String> params){
Result<SysIntegralStatement> result = new Result<SysIntegralStatement>();
try {
//簽名校驗(yàn)
String msgDigest = params.get("msgDigest");//簽名
String msgData = params.get("msgData");
String timeStamp = params.get("timeStamp");
String secret = params.get("secret");// 秘鑰
String sign = SignUtil.sign(msgData+"wf8la1tw7p9o2xz",timeStamp);//wf8la1tw7p9o2xz為簽名混淆值
if (!msgDigest.equals(sign)) {
return result.setCode(1006).setReason("數(shù)字簽名無效");
}
if (Common.isEmpty(secret)) {//先簽名后冪等校驗(yàn)
return result.setCode(1001).setReason("密鑰不能為空");
}
/**
* 冪等校驗(yàn)
* 1.同一個(gè)用戶操作同一個(gè)退貨單一分鐘內(nèi)操作該單據(jù)視為重復(fù)操作(此秘鑰已通過特殊處理)
*/
String value = redistempalte.opsForValue().get(secret);
if (Common.isNotEmpty(value)) {
logger.error("重復(fù)請求 secret={}",value);
return result.setCode(1007).setReason("重復(fù)請求");
}
redistempalte.opsForValue().set(secret, "1",60,TimeUnit.SECONDS);//設(shè)置緩存一分鐘
return service.refundDeductionPoints(params);
} catch (Exception e) {
logger.error("添加積分流水異常", e);
return result.setCode(ErrorCodes.BUSINESS_ERROR).setReason("生成積分流水失敗");
}
}
接口冪等性校驗(yàn)
此接口做冪等性校驗(yàn),冪等性校驗(yàn)常見解決方案有很多,可以自行根據(jù)實(shí)際情況選擇,
說到冪等首先要先了解什么是冪等
概念:
冪等(idempotent、idempotence)是一個(gè)數(shù)學(xué)與計(jì)算機(jī)學(xué)概念,常見于抽象代數(shù)中。
在編程中.一個(gè)冪等操作的特點(diǎn)是其任意多次執(zhí)行所產(chǎn)生的影響均與一次執(zhí)行的影響相同。冪等函數(shù),或冪等方法,是指可以使用相同參數(shù)重復(fù)執(zhí)行,并能獲得相同結(jié)果的函數(shù)。
這些函數(shù)不會(huì)影響系統(tǒng)狀態(tài),也不用擔(dān)心重復(fù)執(zhí)行會(huì)對系統(tǒng)造成改變。例如,“getUsername()和setTrue()”函數(shù)就是一個(gè)冪等函數(shù).
冪等性, 通俗的說就是一個(gè)接口, 多次發(fā)起同一個(gè)請求, 必須保證操作只能執(zhí)行一次,比如:
訂單接口, 不能多次創(chuàng)建訂單
支付接口, 重復(fù)支付同一筆訂單只能扣一次錢
支付寶回調(diào)接口, 可能會(huì)多次回調(diào), 必須處理重復(fù)回調(diào)
普通表單提交接口, 因?yàn)榫W(wǎng)絡(luò)超時(shí)等原因多次點(diǎn)擊提交, 只能成功一次
等等
解決方案常見的幾種方式
唯一索引 – 防止新增臟數(shù)據(jù)
token機(jī)制 – 防止頁面重復(fù)提交
悲觀鎖 – 獲取數(shù)據(jù)的時(shí)候加鎖(鎖表或鎖行)
樂觀鎖 – 基于版本號version實(shí)現(xiàn), 在更新數(shù)據(jù)那一刻校驗(yàn)數(shù)據(jù)
分布式鎖 – redis(jedis、redisson)或zookeeper實(shí)現(xiàn)
狀態(tài)機(jī) – 狀態(tài)變更, 更新數(shù)據(jù)時(shí)判斷狀態(tài)
如果有小伙伴不理解什么是冪等可以看看官方是解釋
實(shí)現(xiàn)類ServiceImpl
@Transactional
@Override
public Result<SysIntegralStatement> refundDeductionPoints(Map<String, String> params) {
String msgData = params.get("msgData");
ParamIntegral entity = new Gson().fromJson(msgData, ParamIntegral.class);
if (Common.isNull(entity)) {
return new Result<SysIntegralStatement>().setCode(ErrorCodes.INNER_ERROR).setReason("請求參數(shù)不能為空");
}
if (Common.isEmpty(entity.getBitems())) {
return new Result<SysIntegralStatement>().setCode(ErrorCodes.INNER_ERROR).setReason("請求參數(shù)不能為空");
}
int row = 0;
for (ParamIntegral bitem : entity.getBitems()) {
if (Common.isEmpty(bitem.getDdh())) {
return new Result<SysIntegralStatement>().setCode(ErrorCodes.INNER_ERROR).setReason("訂單號為必傳參數(shù)");
}
if (null == bitem.getJfz()) {
return new Result<SysIntegralStatement>().setCode(ErrorCodes.INNER_ERROR).setReason("扣減積分不能為空");
}
List<MallOrderInfo> orderInfo = mallOrderInfoMapper.selectByDdh(bitem.getDdh());
if (orderInfo == null) {
return new Result<SysIntegralStatement>().setCode(ErrorCodes.INNER_ERROR)
.setReason("訂單號為" + bitem.getDdh() + "沒有此訂單請聯(lián)系客服核對信息。");
}
if (orderInfo != null && orderInfo.size() > 1) {
return new Result<SysIntegralStatement>().setCode(ErrorCodes.INNER_ERROR)
.setReason("訂單號為" + bitem.getDdh() + "有多個(gè)相同訂單請聯(lián)系客服核對信息。");
}
if (!"E".equals(orderInfo.get(0).getDdzt())) {
return new Result<SysIntegralStatement>().setCode(ErrorCodes.INNER_ERROR)
.setReason("訂單號為" + bitem.getDdh() + "未確認(rèn)收貨還沒產(chǎn)生積分不允許退貨。");
}
SysIntegral integral = Common.first(integralMapper.selectByMdbm(orderInfo.get(0).getMdbm()));
if (integral == null) {
return new Result<SysIntegralStatement>().setCode(ErrorCodes.INNER_ERROR)
.setReason("門店編碼為" + orderInfo.get(0).getMdbm() + "積分匯總沒有找到此門店,請聯(lián)系客服核實(shí)");
}
BigDecimal kyjf = BigDecimal.ZERO;
if (entity.getReturnGoods() == true) {
// 可用積分小于扣減積分不夠扣ERP使用前抵扣
if (bitem.getJfz().compareTo(integral.getKyjf()) == 1) {
kyjf = BigDecimal.ZERO;
} else {
// 可用積分 = 當(dāng)前可用積分-扣減積分
kyjf = Common.nvl(integral.getKyjf(), BigDecimal.ZERO).subtract(bitem.getJfz());
}
} else {
// 可用積分 = 當(dāng)前可用積分+退還積分
kyjf = Common.nvl(integral.getKyjf(), BigDecimal.ZERO).add(bitem.getJfz());
}
// 更新積分匯總
SysIntegral dataMap = new SysIntegral();
dataMap.setIntegralId(integral.getIntegralId());
dataMap.setMdbm(integral.getMdbm());
dataMap.setKyjf(kyjf);
dataMap.setUpdateTime(new Date());
dataMap.setUpdateUser(entity.getUserName());
dataMap.setUpdateUserid(entity.getUserId().intValue());
row = integralMapper.updateByPrimaryKeySelective(dataMap);
if (row == 0) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return new Result<SysIntegralStatement>().setCode(ErrorCodes.INNER_ERROR).setReason("更新積分失敗");
}
//推送到ERP門店信息
BdMdxxH mdxx =new BdMdxxH();
mdxx.setMdbm(integral.getMdbm());
mdxx.setMdjf(kyjf);
com.lkfs.cw.common.Result<BdMdxxH> bdMdxxh = dataBaseServiceApi.updateStorePoints(mdxx);
if (!bdMdxxh.isComplete()) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return new Result<SysIntegralStatement>().setCode(bdMdxxh.getCode()).setReason(bdMdxxh.getReason());
}
SysIntegralStatement statement = new SysIntegralStatement();
if (entity.getReturnGoods() == true) {
statement.setJfz(bitem.getJfz().negate());// 消費(fèi)的積分值
if (bitem.getJfz().compareTo(integral.getKyjf()) == 1) {// 可用積分小于扣減積分不夠扣ERP使用前抵扣
statement.setTzhjfz(BigDecimal.ZERO);// 調(diào)整后積分值
} else {
statement.setTzhjfz(Common.nvl(integral.getKyjf(), BigDecimal.ZERO).subtract(bitem.getJfz()));// 調(diào)整后積分值
}
statement.setJfxflx("E");// 積分支出
statement.setXxsm("退貨扣減積分(訂單號為:" + bitem.getDdh() + "," + "退貨單號為:" + entity.getDjh() + ")" + "已扣除:"
+ bitem.getJfz().negate() + ":積分");
} else {// 取消退貨
statement.setJfxflx("I");// 積分收入
statement.setJfz(bitem.getJfz());// 取消退貨把積分贈(zèng)送回來
statement.setTzhjfz(Common.nvl(integral.getKyjf(), BigDecimal.ZERO).add(bitem.getJfz()));// 調(diào)整后積分值
statement.setXxsm("取消退貨(訂單號為:" + bitem.getDdh() + "," + "退貨單號為:" + entity.getDjh() + ")" + "已退還:"
+ bitem.getJfz() + ":積分");
}
statement.setIntegralId(integral.getIntegralId());// 該門店積分編碼
statement.setTzqjfz(integral.getKyjf());// 調(diào)整前積分值
statement.setDdh(entity.getDdh());
statement.setCreateTime(new Date());// 流水生成時(shí)間
statement.setCreateUser(entity.getUserName());
statement.setCreateUserid(entity.getUserId().intValue());
statement.setJftz("T");// 積分扣減為T
statement.setZt("Y");// 狀態(tài) Y:有效
row = mapper.insert(statement);
if (row == 0) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return new Result<SysIntegralStatement>().setCode(ErrorCodes.INNER_ERROR).setReason("插入積分流水失敗");
}
}
return new Result<SysIntegralStatement>().setCode(ErrorCodes.SUCCESS).setReason("操作成功");
}
第三方調(diào)用接口Api實(shí)現(xiàn)類
模擬第三方合作方調(diào)用接口
//此方式以上已寫了封裝信息就不一一展示了,可以根據(jù)實(shí)際情況自行操作
private void pushIntegral(Long djlsh) {
FiSjysjsH fiSjysjsh = mapper.selectByPrimaryKey(djlsh);
//訂單退貨調(diào)用某某退貨扣減積分接口
List<FiSjysjsB> sjysjsbList = bmapper.selectByKhddh(djlsh);
if (sjysjsbList != null && sjysjsbList.size() > 0) {
List<ParamIntegral> list = new ArrayList<ParamIntegral>();
for (FiSjysjsB bitem : sjysjsbList) {
ParamIntegral temp = new ParamIntegral();
temp.setDdh(bitem.getKhddh());
temp.setJfz(bitem.getJfz());
list.add(temp);
}
ParamIntegral param = new ParamIntegral();
param.setBitems(list);
param.setDjh(fiSjysjsh.getDjh());
param.setUserId(AppRealm.getCurrentUser().getUserId());
param.setUserName(AppRealm.getCurrentUser().getUserName());
if (new Short("1").equals(fiSjysjsh.getLocked())) {
param.setReturnGoods(true);
}else {
param.setReturnGoods(false);
}
String msgData = new Gson().toJson(param).toString();
Map<String, String> params = new HashMap<String, String>();
String timeStamp = String.valueOf(System.currentTimeMillis());//時(shí)間戳
params.put("timeStamp", timeStamp);
params.put("msgData", msgData);
params.put("msgDigest", SignUtil.sign(msgData+"wf8la1tw7p9o2xz", timeStamp));//生成簽名第二個(gè)值暫定(wf8la1tw7p9o2xz簽名混淆值)
params.put("secret",IDEMPOTENT_SECRET_PREFIX + fiSjysjsh.getDjh() + AppRealm.getCurrentUser().getUserId()+param.getReturnGoods() );//自定義密鑰 做冪等校驗(yàn)
String result = HttpCilent.post(B2B_URL, params); //發(fā)送http post請求
B2bIntegralResponse res = new Gson().fromJson(result, B2bIntegralResponse.class);
if (null == res) {
throw new RuntimeException("調(diào)用積分接口系統(tǒng)異常");
}
if (res.getCode() != 0) {//接口返回失敗異常代碼提示
throw new RuntimeException("調(diào)用積分接口發(fā)生異常,異常代碼為:"+res.getCode()+"異常信息為:"+res.getReason());
}
}
}
生成簽名工具類
package com.cy.xgsm.util;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
/**
*
* @author Dylan
*
*/
public class SignUtil {
private static final Logger log = LoggerFactory.getLogger(SignUtil.class);
/**
*
*/
public static String sign(String str, String secret) {
StringBuilder enValue = new StringBuilder();
enValue.append(secret);
enValue.append(str);
enValue.append(secret);
return encryptByMD5(enValue.toString());
}
private static String encryptByMD5(String data) {
String re_md5 = new String();
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(data.getBytes());
byte b[] = md.digest();
int i;
StringBuffer buf = new StringBuffer();
for (int offset = 0; offset < b.length; offset++) {
i = b[offset];
if (i < 0)
i += 256;
if (i < 16)
buf.append("0");
buf.append(Integer.toHexString(i));
}
re_md5 = buf.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return re_md5.toUpperCase();
}
public static String compare(String jsonStr,String secret ){
JsonParser jsonParser = new JsonParser();
JsonObject jsonObject = jsonParser.parse(jsonStr).getAsJsonObject();
String sign1 = "null";
JsonElement signElement = jsonObject.remove("sign");
if( signElement != null ){
sign1 = signElement.getAsString();
}
log.info("sign1: " + sign1);
StringBuilder enValue = new StringBuilder();
enValue.append(secret);
enValue.append(jsonObject.toString());
enValue.append(secret);
String sign2 = encryptByMD5(enValue.toString());
jsonObject.addProperty("sign", sign2);
return jsonObject.toString();
}
}
HttpCilent工具類
這個(gè)工具類在我之前的文章也有但是沒有把這個(gè)方式的放上去,如果有需要用到可直接把一下代碼復(fù)制到這個(gè)Http工具類(見文末) 最后即可直接使用。
/**
* 發(fā)送post請求
* @param url 目的url
* @param parameters 參數(shù)
* @return
*/
public static String post(String url, Map<String, String> parameters) {
String result = "";// 返回的結(jié)果
BufferedReader in = null;// 讀取響應(yīng)輸入流
PrintWriter out = null;
StringBuffer sb = new StringBuffer();// 處理請求參數(shù)
String params = "";// 編碼之后的參數(shù)
try {
// 編碼請求參數(shù)
if (parameters.size() == 1) {
for (String name : parameters.keySet()) {
sb.append(name)
.append("=")
.append(java.net.URLEncoder.encode(
parameters.get(name), "UTF-8"));
}
params = sb.toString();
} else {
for (String name : parameters.keySet()) {
sb.append(name)
.append("=")
.append(java.net.URLEncoder.encode(
parameters.get(name), "UTF-8")).append("&");
}
String temp_params = sb.toString();
params = temp_params.substring(0, temp_params.length() - 1);
}
// 創(chuàng)建URL對象
java.net.URL connURL = new java.net.URL(url);
// 打開URL連接
java.net.HttpURLConnection httpConn = (java.net.HttpURLConnection) connURL
.openConnection();
// 設(shè)置通用屬性
httpConn.setRequestProperty("Accept", "*/*");
httpConn.setRequestProperty("Connection", "Keep-Alive");
httpConn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
httpConn.setRequestProperty("User-Agent",
"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)");
// 設(shè)置POST方式
httpConn.setDoInput(true);
httpConn.setDoOutput(true);
// 獲取HttpURLConnection對象對應(yīng)的輸出流
out = new PrintWriter(httpConn.getOutputStream());
// 發(fā)送請求參數(shù)
out.write(params);
// flush輸出流的緩沖
out.flush();
// 定義BufferedReader輸入流來讀取URL的響應(yīng),設(shè)置編碼方式
in = new BufferedReader(new InputStreamReader(
httpConn.getInputStream(), "UTF-8"));
String line;
// 讀取返回的內(nèi)容
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
return result;
}
附:分享一個(gè)獲取IP工具類
在日常開發(fā)中,經(jīng)常會(huì)遇到需要記錄IP的需求,接下來分享一個(gè)獲取IP工具類,希望對小伙伴有幫助
package com.cy.xgsm.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetAddress;
import java.net.UnknownHostException;
import javax.servlet.http.HttpServletRequest;
/**
* 獲取IP工具類
* @author Dylan
*
*/
public class IpUtils {
private static Logger logger = LoggerFactory.getLogger(IpUtils.class);
/**
* 獲取IP地址
* 使用Nginx等反向代理軟件, 則不能通過request.getRemoteAddr()獲取IP地址
* 如果使用了多級反向代理的話,X-Forwarded-For的值并不止一個(gè),而是一串IP地址,X-Forwarded-For中第一個(gè)非unknown的有效IP字符串,則為真實(shí)IP地址
*/
public static String getIpAddr(HttpServletRequest request)
{
if (request == null)
{
return "unknown";
}
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
ip = request.getHeader("X-Forwarded-For");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
ip = request.getRemoteAddr();
}
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
}
public static boolean internalIp(String ip)
{
byte[] addr = textToNumericFormatV4(ip);
return internalIp(addr) || "127.0.0.1".equals(ip);
}
private static boolean internalIp(byte[] addr)
{
if (StringUtils.isNull(addr) || addr.length < 2)
{
return true;
}
final byte b0 = addr[0];
final byte b1 = addr[1];
final byte section1 = 0x0A;
final byte section2 = (byte) 0xAC;
final byte section3 = (byte) 0x10;
final byte section4 = (byte) 0x1F;
final byte section5 = (byte) 0xC0;
final byte section6 = (byte) 0xA8;
switch (b0)
{
case section1:
return true;
case section2:
if (b1 >= section3 && b1 <= section4)
{
return true;
}
case section5:
switch (b1)
{
case section6:
return true;
default:
return false;
}
default:
return false;
}
}
/**
* 將IPv4地址轉(zhuǎn)換成字節(jié)
*
* @param text IPv4地址
* @return byte 字節(jié)
*/
public static byte[] textToNumericFormatV4(String text)
{
if (text.length() == 0)
{
return null;
}
byte[] bytes = new byte[4];
String[] elements = text.split("\\.", -1);
try
{
long l;
int i;
switch (elements.length)
{
case 1:
l = Long.parseLong(elements[0]);
if ((l < 0L) || (l > 4294967295L)){
return null;
}
bytes[0] = (byte) (int) (l >> 24 & 0xFF);
bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
bytes[3] = (byte) (int) (l & 0xFF);
break;
case 2:
l = Integer.parseInt(elements[0]);
if ((l < 0L) || (l > 255L)){
return null;
}
bytes[0] = (byte) (int) (l & 0xFF);
l = Integer.parseInt(elements[1]);
if ((l < 0L) || (l > 16777215L)){
return null;
}
bytes[1] = (byte) (int) (l >> 16 & 0xFF);
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
bytes[3] = (byte) (int) (l & 0xFF);
break;
case 3:
for (i = 0; i < 2; ++i)
{
l = Integer.parseInt(elements[i]);
if ((l < 0L) || (l > 255L)){
return null;
}
bytes[i] = (byte) (int) (l & 0xFF);
}
l = Integer.parseInt(elements[2]);
if ((l < 0L) || (l > 65535L)){
return null;
}
bytes[2] = (byte) (int) (l >> 8 & 0xFF);
bytes[3] = (byte) (int) (l & 0xFF);
break;
case 4:
for (i = 0; i < 4; ++i)
{
l = Integer.parseInt(elements[i]);
if ((l < 0L) || (l > 255L)){
return null;
}
bytes[i] = (byte) (int) (l & 0xFF);
}
break;
default:
return null;
}
}
catch (NumberFormatException e)
{
return null;
}
return bytes;
}
public static String getHostIp()
{
try
{
return InetAddress.getLocalHost().getHostAddress();
}
catch (UnknownHostException e)
{
}
return "127.0.0.1";
}
public static String getHostName()
{
try
{
return InetAddress.getLocalHost().getHostName();
}
catch (UnknownHostException e)
{
}
return "未知";
}
}
總結(jié)
到此這篇關(guān)于Java如何提供給第三方使用接口方法的文章就介紹到這了,更多相關(guān)Java提供接口給第三方使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JSP頁面pageEncoding和contentType屬性
有關(guān)于JSP頁面中pageEncoding和contentType屬性。2013-04-04
SpringBoot war包部署到Tomcat服務(wù)器
這篇文章主要介紹了SpringBoot war包部署到Tomcat服務(wù)器,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-03-03
Java線程的創(chuàng)建介紹及實(shí)現(xiàn)方式示例
這篇文章主要為大家介紹了Java線程的創(chuàng)建介紹及實(shí)現(xiàn)方式示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09
Java實(shí)現(xiàn)快速排序和堆排序的示例代碼
這篇文章主要為大家詳細(xì)介紹了快速排序和堆排序的多種語言的實(shí)現(xiàn)(JavaScript、Python、Go語言、Java、C++),感興趣的小伙伴可以了解一下2022-12-12
Java超詳細(xì)講解設(shè)計(jì)模式中的命令模式
命令模式是將一個(gè)請求封裝為一個(gè)對象,從而可用不同的請求對客戶進(jìn)行參數(shù)化,對請求排隊(duì)或者對請求做日志記錄,以及可以支持撤銷的操作2022-04-04
Spring Cloud重試機(jī)制與各組件的重試總結(jié)
這篇文章主要給大家介紹了關(guān)于Spring Cloud中重試機(jī)制與各組件的重試的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11
Java TimedCache 帶時(shí)間緩存工具類詳解使用
工具類是包含集合框架、遺留的 collection 類、事件模型、日期和時(shí)間設(shè)施、國際化和各種實(shí)用工具類(字符串標(biāo)記生成器、隨機(jī)數(shù)生成器和位數(shù)組、日期Date類、堆棧Stack類、向量Vector類等)。集合類、時(shí)間處理模式、日期工具等各類常用工具包,本文將介紹帶時(shí)間緩存工具類2021-10-10

