java后臺實現(xiàn)支付寶支付接口和支付寶訂單查詢接口(前端為APP)
最近項目APP需要接入微信、支付寶支付功能,在分配開發(fā)任務(wù)時,聽說微信支付接口比支付寶支付接口要難實現(xiàn),由于我開發(fā)經(jīng)驗不是那么豐富(現(xiàn)工作經(jīng)驗1年半)且未接觸過支付接口開發(fā),組里剛好又有支付接口的老司機,所以很自然把簡單的支付寶接口開發(fā)任務(wù)交給了我,看來開發(fā)組的組長還是很好人的嘛.....,廢話就不多說了,我們開始吧!
實現(xiàn)支付寶接口詳細過程
1.去支付寶官網(wǎng)申請公司企業(yè)賬號并開通一個應(yīng)用,在應(yīng)用里簽約APP支付功能
具體的申請截圖步驟,在這里我就不詳細說了,因為這不是文章的重點,可參考支付寶官網(wǎng)。
經(jīng)過這一步,我們可以得過開發(fā)中需要用到的幾個參數(shù)
①商戶appid ②商戶公鑰、私鑰 ③支付寶公鑰 ④支付寶網(wǎng)關(guān)地址
解釋一下這幾個參數(shù):
1.商戶appid是識別商戶的唯一ID,是讓支付寶識別,我們到底是哪一個商戶,這樣支付寶就能識別商戶對應(yīng)的賬號、用戶號、收款賬號...等等一系列信息。
2.商戶公鑰、私鑰以及支付寶公鑰這3個參數(shù)是對商戶系統(tǒng)與支付寶進行信息交互的數(shù)字簽名用的,相信各位大學里也有學過關(guān)于數(shù)字簽名的一些知識,在這里,我就簡單說一下我理解的過程:首先是商戶系統(tǒng)需要給支付寶發(fā)送信息(支付、查詢等等....),涉及錢方面,咱們當前要謹慎一點對吧,所以我們需要對發(fā)送之前的信息加把鎖(用商戶私鑰進行簽名),然后再發(fā)送給支付寶。支付寶收到商戶發(fā)送的信息之后,發(fā)現(xiàn)上了把鎖,那肯定得要一把鑰匙(商戶公鑰)來解鎖對吧,所以商戶在跟支付寶簽約APP支付功能的時候,就得把這把鑰匙上傳給支付寶了,支付寶就可以用商戶的公鑰進行解鎖了。反過來也是一樣,支付寶需要發(fā)送信息給商戶信息,先用支付寶的私鑰進行簽名,再發(fā)送給商戶系統(tǒng),商戶系統(tǒng)收到支付寶反饋過來的信息后,再用支付寶的公鑰進行解密。在這里我們并沒有用到支付寶的私鑰,所以我們并不需要得到支付寶的私鑰。這里放一個生成私鑰公鑰的支付寶官方工具
3.支付寶網(wǎng)關(guān)地址,是用來配置發(fā)送給支付寶的網(wǎng)關(guān)地址的。
2.將支付寶的SDK集成到項目系統(tǒng)里
支付寶的SDK指的就是支付寶提供的工具Jar包給我們開發(fā)者,SDK封裝了大量的基礎(chǔ)功能,使我們可以快速開發(fā)支付寶接口。這也是我在前面說的比微信支付接口更容易實現(xiàn)的原因。獲取支付寶SDK地址:支付寶SDK下載地址,這里我選擇JAVA的SDK。下載解壓后的SDK得到:
alipay-sdk-java20180122110032.jar、commons-logging-1.1.1.jar是我們需要導(dǎo)入到項目里的,因為項目后臺的大致的架構(gòu)是maven+springBoot+jpa,所以我們需要從maven里導(dǎo)入jar包,首先是導(dǎo)入commons-logging-1.1.1.jar,不用多說,咱直接在pom.xml里加上:
<dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>1.1.1</version> </dependency>
然后是alipay-sdk-java20180122110032.jar,如果你也像上面一樣直接加入到pom.xml,會發(fā)現(xiàn),咦,怎么一直下載不下來。當然alipy的包在線上的maven倉庫并沒有,所以我們需要導(dǎo)入到本地的maven倉庫。前提是配置好maven的環(huán)境變量,將包放在G:\alipay\sdk下,然后打開dos窗口,cd進入到G:\alipay\sdk下,執(zhí)行maven如下命令:
mvn install:install-file -DgroupId=com.alipay -DartifactId=sdk-java -Dversion=20180122110032 -Dpackaging=jar -Dfile=alipay-sdk-java20180122110032.jar
導(dǎo)入成功后,在項目的pom.xml里繼續(xù)添加
<!-- 支付寶SDK --> <dependency> <groupId>com.alipay</groupId> <artifactId>sdk-java</artifactId> <version>20180122110032</version> </dependency>
到此,我們就順利把支付寶SDK集成到項目里,是不是有點小興奮,我們很快可以開發(fā)了!
3.看支付寶提供的API和網(wǎng)上各位牛人總結(jié)的經(jīng)驗,后臺使用支付寶的SDK與支付寶進行交互
先看一下支付寶支付流程的圖:
首先,我們來理一理開發(fā)的思路,按照我當前項目的需求,關(guān)于支付這一塊大概操作流程是:用戶在APP上選好要購買的商品,點擊“立即購買”,跳轉(zhuǎn)到訂單詳細頁面。選擇支付方式,點擊“確定支付”跳轉(zhuǎn)到支付寶APP,付款完成后,跳轉(zhuǎn)回APP,完成支付。這個過程,當用戶點擊“確定支付”時,APP需要調(diào)用商戶后臺接口。
這時候就是我們所需要做的事情:先是生成商戶系統(tǒng)一筆未支付的訂單,獲得商戶訂單ID(商戶系統(tǒng)生成)和訂單的一些其他信息,然后再調(diào)用支付寶的SDK提供的數(shù)字簽名方法,將需要傳給支付寶的信息進行加簽,然后把加簽后的字符串返回給APP。APP拉起支付寶APP,再把這個加簽的字符串傳給支付寶,完成支付。APP接收到同步通知后,還需要再次調(diào)用商戶后臺的接口(雖然同步通知也有付款情況,但需要以后臺通知為準),校驗訂單最終的付款情況。按照支付寶API上所說,當完成支付后,支付寶會做2個操作,一個是同步返回信息給APP,一個是異步通知商戶后臺返回支付狀態(tài)等信息,并且最終的支付結(jié)果是以異步通知為準。所以我們還需要考慮到一點,就是當用戶支付成功之后,商戶系統(tǒng)暫時沒有接收到支付寶的異步通知時。我們需要拿著這個商戶訂單ID主動調(diào)用SDK支付寶的查詢接口,去獲取該訂單的支付情況,并最終返回給APP。這個查詢的接口應(yīng)該是給APP收到同步通知后,請求商戶系統(tǒng)后臺進行校驗的時候調(diào)用的。
根據(jù)我們上面思考所得,后臺只需要對外提供3個接口即可
1.用戶點擊“立即購買”時調(diào)用商戶后臺接口,后臺返回加簽后的訂單信息字符串
2.在支付完成之后,支付寶異步通知商戶后臺訂單的付款情況
3.在支付完成之后,跳轉(zhuǎn)回APP時,APP調(diào)用商戶后臺進行最終付款校驗
想通想明白之后,終于接一下我們要敲代碼了,哈哈哈哈
首先,我們來準備一下需要傳給支付寶SDK的公共基本參數(shù),我把參數(shù)放到一個單獨的類里,你也可以放到數(shù)據(jù)庫里,代碼如下:
public class AlipayConfig { // 1.商戶appid //public static String APPID = "2017..."; //2.私鑰 pkcs8格式的 public static String RSA_PRIVATE_KEY ="MIIEwAIBADANBg....."; // 3.支付寶公鑰 public static String ALIPAY_PUBLIC_KEY = "MIIBIjANBgkq....."; // 4.服務(wù)器異步通知頁面路徑 需http://或者https://格式的完整路徑,不能加?id=123這類自定義參數(shù),必須外網(wǎng)可以正常訪問 public static String notify_url = "http://www.xxx.com/alipay/notify_url.do"; //5.頁面跳轉(zhuǎn)同步通知頁面路徑 需http://或者https://格式的完整路徑,不能加?id=123這類自定義參數(shù),必須外網(wǎng)可以正常訪問 商戶可以自定義同步跳轉(zhuǎn)地址 public static String return_url = "http://www.xxx.com/alipay/return_url.do"; // 6.請求支付寶的網(wǎng)關(guān)地址 public static String URL = "https://openapi.alipay.com/gateway.do"; // 7.編碼 public static String CHARSET = "UTF-8"; // 8.返回格式 public static String FORMAT = "json"; // 9.加密類型 public static String SIGNTYPE = "RSA2"; }
1.實現(xiàn)第一個接口:用戶點擊“立即購買”時調(diào)用商戶后臺接口,后臺返回加簽后的訂單信息字符串。我把主要的處理邏輯寫在Service層了,Controller層直接調(diào)用就可以,這里就不放Controller層的代碼了
生成商戶訂單的代碼,我就不放了,這個根據(jù)各自的業(yè)務(wù)需求來做,生成后訂單之后,把訂單信息傳進來該方法進行處理,返回加簽后的字符串,直接返回給APP即可,代碼如下:
/** * 獲取支付寶加簽后臺的訂單信息字符串 * * @param request * @return */ @Override @Transactional(propagation = Propagation.REQUIRED) public String getAliPayOrderStr(OrderTest orderTest) { //最終返回加簽之后的,app需要傳給支付寶app的訂單信息字符串 String orderString = ""; logger.info("==================支付寶下單,商戶訂單號為:"+orderTest.getOutTradeNo()); //創(chuàng)建商戶支付寶訂單(因為需要記錄每次支付寶支付的記錄信息,單獨存一個表跟商戶訂單表關(guān)聯(lián),以便以后查證) AlipaymentOrder alipaymentOrder=new AlipaymentOrder(); alipaymentOrder.setClubOrderId(orderTest.getId().toString());//商家訂單主鍵 alipaymentOrder.setOutTradeNo(orderTest.getOutTradeNo());//商戶訂單號 alipaymentOrder.setTradeStatus((byte) 0);//交易狀態(tài) alipaymentOrder.setTotalAmount(Double.parseDouble(orderTest.getTotalAmount()));//訂單金額 alipaymentOrder.setReceiptAmount(0.00);//實收金額 alipaymentOrder.setInvoiceAmount(0.00);//開票金額 alipaymentOrder.setBuyerPayAmount(0.00);//付款金額 alipaymentOrder.setRefundFee(0.00); //總退款金額 try{ //實例化客戶端(參數(shù):網(wǎng)關(guān)地址、商戶appid、商戶私鑰、格式、編碼、支付寶公鑰、加密類型),為了取得預(yù)付訂單信息 AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID, AlipayConfig.RSA_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET, AlipayConfig.ALIPAY_PUBLIC_KEY,AlipayConfig.SIGNTYPE); //實例化具體API對應(yīng)的request類,類名稱和接口名稱對應(yīng),當前調(diào)用接口名稱:alipay.trade.app.pay AlipayTradeAppPayRequest ali_request = new AlipayTradeAppPayRequest(); //SDK已經(jīng)封裝掉了公共參數(shù),這里只需要傳入業(yè)務(wù)參數(shù)。以下方法為sdk的model入?yún)⒎绞? AlipayTradeAppPayModel model = new AlipayTradeAppPayModel(); //業(yè)務(wù)參數(shù)傳入,可以傳很多,參考API //model.setPassbackParams(URLEncoder.encode(request.getBody().toString())); //公用參數(shù)(附加數(shù)據(jù)) model.setBody(orderTest.getBody()); //對一筆交易的具體描述信息。如果是多種商品,請將商品描述字符串累加傳給body。 model.setSubject(orderTest.getSubjecy()); //商品名稱 model.setOutTradeNo(orderTest.getOutTradeNo()); //商戶訂單號(自動生成) // model.setTimeoutExpress("30m"); //交易超時時間 model.setTotalAmount(orderTest.getTotalAmount()); //支付金額 model.setProductCode("QUICK_MSECURITY_PAY"); //銷售產(chǎn)品碼(固定值) ali_request.setBizModel(model); logger.info("====================異步通知的地址為:"+alipayment.getNotifyUrl()); ali_request.setNotifyUrl(AlipayConfig.notify_url); //異步回調(diào)地址(后臺) ali_request.setReturnUrl(AlipayConfig.return_url); //同步回調(diào)地址(APP) // 這里和普通的接口調(diào)用不同,使用的是sdkExecute AlipayTradeAppPayResponse alipayTradeAppPayResponse = alipayClient.sdkExecute(ali_request); //返回支付寶訂單信息(預(yù)處理) orderString=alipayTradeAppPayResponse.getBody();//就是orderString 可以直接給APP請求,無需再做處理。 this.createAlipayMentOrder(alipaymentOrder);//創(chuàng)建新的商戶支付寶訂單 } catch (AlipayApiException e) { e.printStackTrace(); logger.info("與支付寶交互出錯,未能生成訂單,請檢查代碼!"); } return orderString; }
2.實現(xiàn)第二個接口:在支付完成之后,支付寶異步通知商戶后臺訂單的付款情況,這個是支付寶每隔一段時間來訪問一次的接口,直到你返回success,才會停止訪問,這里我分了2個地方進行調(diào)用
/** * 支付寶支付成功后.異步請求該接口 * @param request * @return * @throws IOException */ @RequestMapping(value="/notify_url",method=RequestMethod.POST) @ResponseBody public String notify(HttpServletRequest request,HttpServletResponse response) throws IOException { logger.info("==================支付寶異步返回支付結(jié)果開始"); //1.從支付寶回調(diào)的request域中取值 //獲取支付寶返回的參數(shù)集合 Map<String, String[]> aliParams = request.getParameterMap(); //用以存放轉(zhuǎn)化后的參數(shù)集合 Map<String, String> conversionParams = new HashMap<String, String>(); for (Iterator<String> iter = aliParams.keySet().iterator(); iter.hasNext();) { String key = iter.next(); String[] values = aliParams.get(key); String valueStr = ""; for (int i = 0; i < values.length; i++) { valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ","; } // 亂碼解決,這段代碼在出現(xiàn)亂碼時使用。如果mysign和sign不相等也可以使用這段代碼轉(zhuǎn)化 // valueStr = new String(valueStr.getBytes("ISO-8859-1"), "uft-8"); conversionParams.put(key, valueStr); } logger.info("==================返回參數(shù)集合:"+conversionParams); String status=alipayMentOrderService.notify(conversionParams); return status; }
/** * 支付寶異步請求邏輯處理 * @param request * @return * @throws IOException */ public String notify(Map<String, String> conversionParams){ logger.info("==================支付寶異步請求邏輯處理"); //簽名驗證(對支付寶返回的數(shù)據(jù)驗證,確定是支付寶返回的) boolean signVerified = false; try { //調(diào)用SDK驗證簽名 signVerified = AlipaySignature.rsaCheckV1(conversionParams, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.CHARSET, AlipayConfig.SIGNTYPE); } catch (AlipayApiException e) { logger.info("==================驗簽失敗 !"); e.printStackTrace(); } //對驗簽進行處理 if (signVerified) { //驗簽通過 //獲取需要保存的數(shù)據(jù) String appId=conversionParams.get("app_id");//支付寶分配給開發(fā)者的應(yīng)用Id String notifyTime=conversionParams.get("notify_time");//通知時間:yyyy-MM-dd HH:mm:ss String gmtCreate=conversionParams.get("gmt_create");//交易創(chuàng)建時間:yyyy-MM-dd HH:mm:ss String gmtPayment=conversionParams.get("gmt_payment");//交易付款時間 String gmtRefund=conversionParams.get("gmt_refund");//交易退款時間 String gmtClose=conversionParams.get("gmt_close");//交易結(jié)束時間 String tradeNo=conversionParams.get("trade_no");//支付寶的交易號 String outTradeNo = conversionParams.get("out_trade_no");//獲取商戶之前傳給支付寶的訂單號(商戶系統(tǒng)的唯一訂單號) String outBizNo=conversionParams.get("out_biz_no");//商戶業(yè)務(wù)號(商戶業(yè)務(wù)ID,主要是退款通知中返回退款申請的流水號) String buyerLogonId=conversionParams.get("buyer_logon_id");//買家支付寶賬號 String sellerId=conversionParams.get("seller_id");//賣家支付寶用戶號 String sellerEmail=conversionParams.get("seller_email");//賣家支付寶賬號 String totalAmount=conversionParams.get("total_amount");//訂單金額:本次交易支付的訂單金額,單位為人民幣(元) String receiptAmount=conversionParams.get("receipt_amount");//實收金額:商家在交易中實際收到的款項,單位為元 String invoiceAmount=conversionParams.get("invoice_amount");//開票金額:用戶在交易中支付的可開發(fā)票的金額 String buyerPayAmount=conversionParams.get("buyer_pay_amount");//付款金額:用戶在交易中支付的金額 String tradeStatus = conversionParams.get("trade_status");// 獲取交易狀態(tài) //支付寶官方建議校驗的值(out_trade_no、total_amount、sellerId、app_id) AlipaymentOrder alipaymentOrder=this.selectByOutTradeNo(outTradeNo); if(alipaymentOrder!=null&&totalAmount.equals(alipaymentOrder.getTotalAmount().toString())&&AlipayConfig.APPID.equals(appId)){ //修改數(shù)據(jù)庫支付寶訂單表(因為要保存每次支付寶返回的信息到數(shù)據(jù)庫里,以便以后查證) alipaymentOrder.setNotifyTime(dateFormat(notifyTime)); alipaymentOrder.setGmtCreate(dateFormat(gmtCreate)); alipaymentOrder.setGmtPayment(dateFormat(gmtPayment)); alipaymentOrder.setGmtRefund(dateFormat(gmtRefund)); alipaymentOrder.setGmtClose(dateFormat(gmtClose)); alipaymentOrder.setTradeNo(tradeNo); alipaymentOrder.setOutBizNo(outBizNo); alipaymentOrder.setBuyerLogonId(buyerLogonId); alipaymentOrder.setSellerId(sellerId); alipaymentOrder.setSellerEmail(sellerEmail); alipaymentOrder.setTotalAmount(Double.parseDouble(totalAmount)); alipaymentOrder.setReceiptAmount(Double.parseDouble(receiptAmount)); alipaymentOrder.setInvoiceAmount(Double.parseDouble(invoiceAmount)); alipaymentOrder.setBuyerPayAmount(Double.parseDouble(buyerPayAmount)); switch (tradeStatus) // 判斷交易結(jié)果 { case "TRADE_FINISHED": // 交易結(jié)束并不可退款 alipaymentOrder.setTradeStatus((byte) 3); break; case "TRADE_SUCCESS": // 交易支付成功 alipaymentOrder.setTradeStatus((byte) 2); break; case "TRADE_CLOSED": // 未付款交易超時關(guān)閉或支付完成后全額退款 alipaymentOrder.setTradeStatus((byte) 1); break; case "WAIT_BUYER_PAY": // 交易創(chuàng)建并等待買家付款 alipaymentOrder.setTradeStatus((byte) 0); break; default: break; } int returnResult=this.updateByPrimaryKey(alipaymentOrder); //更新交易表中狀態(tài) if(tradeStatus.equals("TRADE_SUCCESS")) { //只處理支付成功的訂單: 修改交易表狀態(tài),支付成功 if(returnResult>0){ return "success"; }else{ return "fail"; } }else{ return "fail"; } }else{ logger.info("==================支付寶官方建議校驗的值(out_trade_no、total_amount、sellerId、app_id),不一致!返回fail"); return"fail"; } } else { //驗簽不通過 logger.info("==================驗簽不通過 !"); return "fail"; } }
3.實現(xiàn)第三個接口:在支付完成之后,跳轉(zhuǎn)回APP時,APP調(diào)用商戶后臺進行最終付款校驗。我把主要的處理邏輯寫在Service層了,Controller層直接調(diào)用就可以,這里就不放Controller層的代碼了。
/** * 向支付寶發(fā)起訂單查詢請求 * @param request * @return * @throws IOException */ @Override public Byte checkAlipay(String outTradeNo) { logger.info("==================向支付寶發(fā)起查詢,查詢商戶訂單號為:"+outTradeNo); try { //實例化客戶端(參數(shù):網(wǎng)關(guān)地址、商戶appid、商戶私鑰、格式、編碼、支付寶公鑰、加密類型) AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID, AlipayConfig.RSA_PRIVATE_KEY, AlipayConfig.FORMAT, AlipayConfig.CHARSET, AlipayConfig.ALIPAY_PUBLIC_KEY,AlipayConfig.SIGNTYPE); AlipayTradeQueryRequest alipayTradeQueryRequest = new AlipayTradeQueryRequest(); alipayTradeQueryRequest.setBizContent("{" + "\"out_trade_no\":\""+outTradeNo+"\"" + "}"); AlipayTradeQueryResponse alipayTradeQueryResponse = alipayClient.execute(alipayTradeQueryRequest); if(alipayTradeQueryResponse.isSuccess()){ AlipaymentOrder alipaymentOrder=this.selectByOutTradeNo(outTradeNo); //修改數(shù)據(jù)庫支付寶訂單表 alipaymentOrder.setTradeNo(alipayTradeQueryResponse.getTradeNo()); alipaymentOrder.setBuyerLogonId(alipayTradeQueryResponse.getBuyerLogonId()); alipaymentOrder.setTotalAmount(Double.parseDouble(alipayTradeQueryResponse.getTotalAmount())); alipaymentOrder.setReceiptAmount(Double.parseDouble(alipayTradeQueryResponse.getReceiptAmount())); alipaymentOrder.setInvoiceAmount(Double.parseDouble(alipayTradeQueryResponse.getInvoiceAmount())); alipaymentOrder.setBuyerPayAmount(Double.parseDouble(alipayTradeQueryResponse.getBuyerPayAmount())); switch (alipayTradeQueryResponse.getTradeStatus()) // 判斷交易結(jié)果 { case "TRADE_FINISHED": // 交易結(jié)束并不可退款 alipaymentOrder.setTradeStatus((byte) 3); break; case "TRADE_SUCCESS": // 交易支付成功 alipaymentOrder.setTradeStatus((byte) 2); break; case "TRADE_CLOSED": // 未付款交易超時關(guān)閉或支付完成后全額退款 alipaymentOrder.setTradeStatus((byte) 1); break; case "WAIT_BUYER_PAY": // 交易創(chuàng)建并等待買家付款 alipaymentOrder.setTradeStatus((byte) 0); break; default: break; } this.updateByPrimaryKey(alipaymentOrder); //更新表記錄 return alipaymentOrder.getTradeStatus(); } else { logger.info("==================調(diào)用支付寶查詢接口失??!"); } } catch (AlipayApiException e) { // TODO Auto-generated catch block e.printStackTrace(); } return 0; }
至此,代碼已經(jīng)上完了,里面可能涉及部分業(yè)務(wù)代碼,如果各位需要拿代碼,需要把業(yè)務(wù)代碼換成自己所需要的。
建議:可以邊看API邊進行開發(fā),主要是看我們需要給支付傳什么參數(shù),支付寶可以給我們傳什么參數(shù),不然沒看清除,你會多很多坑要踩的,親試過。
感覺太少圖片了,這里給幾張API的圖片。。你們也可以自己去看 一些支付寶API
4.關(guān)于測試的一些事
支付寶有提供沙箱環(huán)境進行測試所使用,見支付寶沙箱調(diào)試指南。
但是樓主比較有米,直接用真實環(huán)境進行測試,其實測一次0.01元也是挺貴的吧,前提是你的電腦必須訪問外網(wǎng)和能夠被外網(wǎng)所訪問,建議可以找個內(nèi)網(wǎng)穿透的工具。
寫了一整個下午,好累啊,本人新手,如有錯誤,請各位大神指教,希望對大家有用!?。?!也希望大家多多支持腳本之家。
相關(guān)文章

java HashMap和HashTable的區(qū)別詳解

Java中抽象類和接口的區(qū)別_動力節(jié)點Java學院整理

如何使用eclipse搭建maven多module項目(構(gòu)建父子項目)

idea報錯:程序包org.springframework.web.bind.annotation不存在

Netty分布式ByteBuf使用SocketChannel讀取數(shù)據(jù)過程剖析

Redis緩存,泛型集合與json字符串的相互轉(zhuǎn)換實例

深入理解Java基礎(chǔ)之try-with-resource語法糖