Java PC端微信、支付宝扫码支付(一)
Java PC端微信、支付寶掃碼支付(一)
前端時(shí)間寫的項(xiàng)目用到了微信和支付寶的掃碼支付,因?yàn)槭堑谝淮螌?#xff0c;網(wǎng)上關(guān)于支付的資料也不多,所以用的時(shí)間比較長,趁現(xiàn)在工作不是太忙的時(shí)候?qū)σ郧暗墓ぷ骺偨Y(jié)一下。
一、微信
先附上微信的支付文檔地址 https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_3
關(guān)于掃碼支付微信給出了兩種模式,具體官方文檔都有說明,這里用的是模式二。
先說一下我在寫的時(shí)候遇到的一個(gè)問題:在最開始寫好支付的時(shí)候,測試也測試成功了,但是前端就是接收不到支付成功的通知,最后看了下官方文檔,發(fā)現(xiàn)在支付回調(diào)里面支付成功的通知只是通知給支付的用戶,也就是你用微信支付成功后會(huì)跳轉(zhuǎn)到支付成功的界面,但是我們的前端卻是收不到通知的。關(guān)于這個(gè)問題微信官方也給出了相應(yīng)的對(duì)策,就是在用戶發(fā)起支付請(qǐng)求的時(shí)候前端去輪詢微信的查詢訂單的接口,每個(gè)五秒或者三秒一次,直到收到支付成功的通知或者交易關(guān)閉才結(jié)束。這時(shí)候你可能就會(huì)想到這樣做會(huì)不會(huì)對(duì)我們服務(wù)端的壓力過大,但是這也是沒辦法的事情,畢竟我們調(diào)的事第三方的接口。我當(dāng)時(shí)也想到了這個(gè)問題,然后我在京東的下單頁面看了一下人家的請(qǐng)求,發(fā)現(xiàn)京東也是通過輪詢?nèi)ゲ樵冇唵螤顟B(tài),以此來判定訂單是否支付成功。
既然大佬都這樣做了想必也是最好的辦法了。
京東是大概每隔五秒去查詢以此訂單狀態(tài)。
下面是具體的代碼
1.Controller層
//微信支付接口@ApiOperation("微信支付")@PostMapping("/wxPay")public Result wxPay(WeChatParams ps) throws Exception {return tbPaymentRecordService.wxPay(ps);}2.Service層
/*** 微信支付* @param ps* @return* @throws Exception*/Result wxPay(WeChatParams ps) throws Exception;3.Impl
@Overridepublic Result wxPay(WeChatParams ps) throws Exception {TbPaymentAmount tbPaymentAmount = new TbPaymentAmount().selectById(ps.getAmountId());String numberCode = getNumberCode();Long doctorId = Login.userId();ps.setBody("服務(wù)充值");ps.setAttach(doctorId.toString());ps.setAmount(tbPaymentAmount.getAmount().multiply(new BigDecimal("100")).stripTrailingZeros().toPlainString());ps.setOrderNo(numberCode);//ps.setDoctorId(doctorId.toString());ps.setDoctorId(doctorId.toString());String orderId = getNumberCode();//在這里生成訂單信息,此時(shí)訂單是未支付狀態(tài)String urlCode = WeixinPay.getCodeUrl(ps);Map<String,Object> maps=new HashMap<>();maps.put("urlCode",urlCode);maps.put("orderNo",numberCode);return Result.success(maps);}上面是用戶發(fā)起支付請(qǐng)求,生成二維碼鏈接,然后返回給前端,前端有工具可以直接生成二維碼展示在頁面,在這里給前端返回了訂單號(hào),方便前端用來查詢支付的交易狀態(tài),這個(gè)在后面會(huì)說到。在這里注意微信支付的支付金額是以分為單位的,就是在這里ps.setAmount()傳入的金額要把你的實(shí)際金額乘以100。支付寶是沒有這個(gè)問題的。
下面是支付用到的工具
public static Logger lg=Logger.getLogger(WeixinPay.class);/*** 獲取微信支付的二維碼地址* @return* @author chenp* @throws Exception*/public static String getCodeUrl(WeChatParams ps) throws Exception {/*** 賬號(hào)信息*/String appid = WeChatConfig.APPID;//微信服務(wù)號(hào)的appidString mch_id = WeChatConfig.MCHID; //微信支付商戶號(hào)String key = WeChatConfig.APIKEY; // 微信支付的API密鑰String notify_url = WeChatConfig.WECHAT_NOTIFY_URL_PC;//回調(diào)地址【注意,這里必須要使用外網(wǎng)的地址】String ufdoder_url=WeChatConfig.UFDODER_URL;//微信下單API地址String trade_type = "NATIVE"; //類型【網(wǎng)頁掃碼支付】/*** 時(shí)間字符串*/String currTime = PayForUtil.getCurrTime();String strTime = currTime.substring(8, currTime.length());String strRandom = PayForUtil.buildRandom(4) + "";String nonce_str = strTime + strRandom;//修改到期時(shí)間Calendar cal = Calendar.getInstance();cal.setTime(new Date());//設(shè)置起始時(shí)間cal.add(Calendar.MINUTE, 1);//增加一分鐘DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");String format = df.format(cal.getTime());System.out.println(format);/*** 參數(shù)封裝*/SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();packageParams.put("appid", appid);packageParams.put("mch_id", mch_id);packageParams.put("nonce_str", nonce_str);//隨機(jī)字符串packageParams.put("body", ps.body);//支付的商品名稱packageParams.put("out_trade_no", ps.orderNo);//商戶訂單號(hào)【備注:每次發(fā)起請(qǐng)求都需要隨機(jī)的字符串,否則失敗?!縫ackageParams.put("total_fee", ps.amount);//支付金額packageParams.put("spbill_create_ip", PayForUtil.localIp());//客戶端主機(jī)packageParams.put("notify_url", notify_url);packageParams.put("trade_type", trade_type);packageParams.put("attach", ps.attach);//額外的參數(shù)【業(yè)務(wù)類型+會(huì)員ID+支付類型】packageParams.put("time_expire", format);//額外的參數(shù)【業(yè)務(wù)類型+會(huì)員ID+支付類型】String sign = PayForUtil.createSign("UTF-8", packageParams,key); //獲取簽名packageParams.put("sign", sign);String requestXML = PayForUtil.getRequestXml(packageParams);//將請(qǐng)求參數(shù)轉(zhuǎn)換成String類型lg.info("微信支付請(qǐng)求參數(shù)的報(bào)文"+requestXML);String resXml = HttpUtils.postData(ufdoder_url,requestXML); //解析請(qǐng)求之后的xml參數(shù)并且轉(zhuǎn)換成String類型Map map = XMLUtil.doXMLParse(resXml);lg.info("微信支付響應(yīng)參數(shù)的報(bào)文"+resXml);String urlCode = (String) map.get("code_url");return urlCode;} /*** 微信支付需要的一些參數(shù)* @author chenp**/ @Data//此注解代替了set、get方法,如果不想用這個(gè)注解也可以自己寫set、get方法。 public class WeChatParams {@ApiModelProperty(value = "訂單金額",hidden = true)public String amount;//訂單金額【備注:以分為單位】@ApiModelProperty(value = "商品名稱",hidden = true)public String body;//商品名稱@ApiModelProperty(value = "商戶訂單號(hào)",hidden = true)public String orderNo;//商戶訂單號(hào)@ApiModelProperty(value = "附加參數(shù)",hidden = true)public String attach;//附加參數(shù)@ApiModelProperty(value = "醫(yī)生ID",hidden = true)public String doctorId;//會(huì)員ID@ApiModelProperty("繳費(fèi)金額id")public Long amountId;} package com.jgntech.medicine.core.kit.util;import java.net.Inet4Address; import java.net.InetAddress; import java.net.InterfaceAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedMap;import org.apache.log4j.Logger;/*** Created by hcs on 2019/4/30 13:32*/ public class PayForUtil {private static Logger lg=Logger.getLogger(PayForUtil.class);/*** 是否簽名正確,規(guī)則是:按參數(shù)名稱a-z排序,遇到空值的參數(shù)不參加簽名。* @return boolean*/public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {StringBuffer sb = new StringBuffer();Set es = packageParams.entrySet();Iterator it = es.iterator();while(it.hasNext()) {Map.Entry entry = (Map.Entry)it.next();String k = (String)entry.getKey();String v = (String)entry.getValue();if(!"sign".equals(k) && null != v && !"".equals(v)) {sb.append(k + "=" + v + "&");}}sb.append("key=" + API_KEY);//算出摘要String mysign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toLowerCase();String tenpaySign = ((String)packageParams.get("sign")).toLowerCase();return tenpaySign.equals(mysign);}/*** @author chenp* @Description:sign簽名* @param characterEncoding* 編碼格式* @param parameters* 請(qǐng)求參數(shù)* @return*/public static String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {StringBuffer sb = new StringBuffer();Set es = packageParams.entrySet();Iterator it = es.iterator();while (it.hasNext()) {Map.Entry entry = (Map.Entry) it.next();String k = (String) entry.getKey();String v = (String) entry.getValue();if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {sb.append(k + "=" + v + "&");}}sb.append("key=" + API_KEY);String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();return sign;}/*** @author chenp* @Description:將請(qǐng)求參數(shù)轉(zhuǎn)換為xml格式的string* @param parameters* 請(qǐng)求參數(shù)* @return*/public static String getRequestXml(SortedMap<Object, Object> parameters) {StringBuffer sb = new StringBuffer();sb.append("<xml>");Set es = parameters.entrySet();Iterator it = es.iterator();while (it.hasNext()) {Map.Entry entry = (Map.Entry) it.next();String k = (String) entry.getKey();String v = (String) entry.getValue();if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");} else {sb.append("<" + k + ">" + v + "</" + k + ">");}}sb.append("</xml>");return sb.toString();}/*** 取出一個(gè)指定長度大小的隨機(jī)正整數(shù).** @param length* int 設(shè)定所取出隨機(jī)數(shù)的長度。length小于11* @return int 返回生成的隨機(jī)數(shù)。*/public static int buildRandom(int length) {int num = 1;double random = Math.random();if (random < 0.1) {random = random + 0.1;}for (int i = 0; i < length; i++) {num = num * 10;}return (int) ((random * num));}/*** 獲取當(dāng)前時(shí)間 yyyyMMddHHmmss* @author chenp* @return String*/public static String getCurrTime() {Date now = new Date();SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");String s = outFormat.format(now);return s;}/*** 獲取本機(jī)IP地址* @author chenp* @return*/public static String localIp(){String ip = null;Enumeration allNetInterfaces;try {allNetInterfaces = NetworkInterface.getNetworkInterfaces();while (allNetInterfaces.hasMoreElements()) {NetworkInterface netInterface = (NetworkInterface) allNetInterfaces.nextElement();List<InterfaceAddress> InterfaceAddress = netInterface.getInterfaceAddresses();for (InterfaceAddress add : InterfaceAddress) {InetAddress Ip = add.getAddress();if (Ip != null && Ip instanceof Inet4Address) {ip = Ip.getHostAddress();}}}} catch (SocketException e) {lg.warn("獲取本機(jī)Ip失敗:異常信息:"+e.getMessage());}return ip;} } package com.jgntech.medicine.core.config;/*** Created by hcs on 2019/4/30 13:53*/ public class WeChatConfig {/*** 微信服務(wù)號(hào)APPID*/public static String APPID="";/*** 微信支付的商戶號(hào)*/public static String MCHID="";/*** 微信支付的API密鑰*/public static String APIKEY="";/*** 微信支付成功之后的回調(diào)地址【注意:當(dāng)前回調(diào)地址必須是公網(wǎng)能夠訪問的地址】*/public static String WECHAT_NOTIFY_URL_PC="";/*** 微信統(tǒng)一下單API地址*/public static String UFDODER_URL="https://api.mch.weixin.qq.com/pay/unifiedorder";/*** true為使用真實(shí)金額支付,false為使用測試金額支付(1分)*/public static String WXPAY="true";}上面就是生成支付二維碼的代碼了
下面就是支付回調(diào),這個(gè)回調(diào)地址是在WeChatConfig 里面配置的,記住一定是外網(wǎng)可以訪問的地址,如果沒有服務(wù)器可以去百度內(nèi)網(wǎng)穿透工具,我在這里用的是花生殼。
1.Controller層
@ApiOperation("微信支付回調(diào)")@PostMapping("/wxNotify")public void wxNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {tbPaymentRecordService.wechatNotifyUrlPc(request, response);}2.Service層
/*** 微信支付回調(diào)* @param request* @param response* @throws Exception*/void wechatNotifyUrlPc(HttpServletRequest request, HttpServletResponse response) throws Exception;3.Impl
@Overridepublic void wechatNotifyUrlPc(HttpServletRequest request, HttpServletResponse response) throws Exception {//讀取參數(shù)InputStream inputStream;StringBuffer sb = new StringBuffer();inputStream = request.getInputStream();String s;BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));while ((s = in.readLine()) != null) {sb.append(s);}in.close();inputStream.close();//解析xml成mapMap<String, String> m = new HashMap<String, String>();m = XMLUtil.doXMLParse(sb.toString());//過濾空 設(shè)置 TreeMapSortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();Iterator<String> it = m.keySet().iterator();while (it.hasNext()) {String parameter = it.next();String parameterValue = m.get(parameter);String v = "";if (null != parameterValue) {v = parameterValue.trim();}packageParams.put(parameter, v);}// 微信支付的API密鑰String key = WeChatConfig.APIKEY; // keylg.info("微信支付返回回來的參數(shù):" + packageParams);//判斷簽名是否正確if (PayForUtil.isTenpaySign("UTF-8", packageParams, key)) {//------------------------------//處理業(yè)務(wù)開始//------------------------------String resXml = "";//商戶訂單號(hào)String out_trade_no = (String) packageParams.get("out_trade_no");TbOrder to = tbOrderDao.selectTbOrderByOrderNo(out_trade_no);if (to == null || to.getStatus() != 1) {if ("SUCCESS".equals((String) packageParams.get("result_code"))) {// 這里是支付成功//執(zhí)行自己的業(yè)務(wù)邏輯開始(修改訂單狀態(tài))String app_id = (String) packageParams.get("appid");String mch_id = (String) packageParams.get("mch_id");String openid = (String) packageParams.get("openid");String is_subscribe = (String) packageParams.get("is_subscribe");//是否關(guān)注公眾號(hào)//附加參數(shù)【商標(biāo)申請(qǐng)_0bda32824db44d6f9611f1047829fa3b_15460】--【業(yè)務(wù)類型_會(huì)員ID_訂單號(hào)】String attach = (String) packageParams.get("attach");//付款金額【以分為單位】String total_fee = (String) packageParams.get("total_fee");//微信生成的交易訂單號(hào)String transaction_id = (String) packageParams.get("transaction_id");//微信支付訂單號(hào)//支付完成時(shí)間String time_end = (String) packageParams.get("time_end");lg.info("app_id:" + app_id);lg.info("mch_id:" + mch_id);lg.info("openid:" + openid);lg.info("is_subscribe:" + is_subscribe);lg.info("out_trade_no:" + out_trade_no);lg.info("total_fee:" + total_fee);lg.info("額外參數(shù)_attach:" + attach);lg.info("time_end:" + time_end);//執(zhí)行自己的業(yè)務(wù)邏輯結(jié)束lg.info("支付成功");//通知微信.異步確認(rèn)成功.必寫.不然會(huì)一直通知后臺(tái).八次之后就認(rèn)為交易失敗了.resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";} else {lg.info("支付失敗,錯(cuò)誤信息:" + packageParams.get("err_code"));resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"+ "<return_msg><![CDATA[報(bào)文為空]]></return_msg>" + "</xml> ";}} else {lg.info("無需重復(fù)支付");resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";}//------------------------------//處理業(yè)務(wù)完畢//------------------------------BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());out.write(resXml.getBytes());out.flush();out.close();} else {lg.info("通知簽名驗(yàn)證失敗");}}在這里支付成功會(huì)給微信一個(gè)"SUCCESS",但是我們的前端卻是收不到這個(gè)success的,這就需要我們寫一個(gè)查詢接口了,具體的參數(shù)和返回值信息可以查看微信的api文檔 https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_2
查詢接口跟下單接口差不多,可以寫在WeixinPay這個(gè)類里面。
話不多說,直接上代碼
1.Controller層
在這里需要前端把下單接口返回的訂單號(hào)傳過來,在這里我們是用這個(gè)訂單號(hào)查詢訂單的支付狀態(tài),不過還可以用微信的訂單號(hào)(transaction_id)去查,但是據(jù)我了解這個(gè)訂單號(hào)是在用戶支付成功,也就是回調(diào)里面生成的訂單號(hào),如果真是這樣的話在生成訂單的時(shí)候我們就拿不到這個(gè)訂單號(hào)了。
2.Service層
/*** 微信支付訂單查詢* @return* @throws Exception*/Result wxPayExample(HttpServletRequest request,String out_trade_no) throws Exception;3.Impl
@Overridepublic Result wxPayExample(HttpServletRequest request,String out_trade_no) throws Exception {Map<String, String> resultMap = new HashMap();lg.info("*************************調(diào)用支付查詢 start*************************");if( out_trade_no == null || out_trade_no.trim().equals("")){ThrowsKit.error(BizExceptionEnum.VALIDA_ERROR.msg("訂單號(hào)為空"));}//查詢微信支付狀態(tài)try {//TbRechargeRecord tbRechargeRecord = new TbRechargeRecord().selectOne(new EntityWrapper().eq("order_no",out_trade_no));//Map<String, String> map = WeixinPay.queryWeiXinPay(tbRechargeRecord.getVoucherNo());Map<String, String> map = WeixinPay.queryWeiXinPay(out_trade_no);lg.info("js定時(shí)器查詢微信訂單結(jié)果為=="+map);if(map==null||map.isEmpty()){ThrowsKit.error(BizExceptionEnum.VALIDA_ERROR.msg("查詢支付狀態(tài)失敗"));}else{String total_fee = map.get("total_fee");//交易金額resultMap.put("return_code", "1");resultMap.put("total_fee", total_fee);resultMap.put("orderId", out_trade_no);}} catch (Exception e) {e.printStackTrace();ThrowsKit.error(BizExceptionEnum.STATUS_ERROR.msg("查詢支付狀態(tài)失敗"));}return Result.success(resultMap);}4.WeixinPay類
/*** 調(diào)用微信支付查詢接口,返回支付信息* @param orderId* @return* @throws Exception*/public static Map<String, String> queryWeiXinPay(String orderId)throws Exception{Map<String, String> resp = null;MyConfig config = new MyConfig();WXPay wxpay = new WXPay(config,WXPayConstants.SignType.MD5,false);//true為測試環(huán)境Map<String, String> data = new HashMap<String, String>();data.put("out_trade_no", orderId);//訂單號(hào)//data.put("transaction_id", orderId);//微信支付單號(hào)try{resp = wxpay.orderQuery(data);String return_code = (String)resp.get("return_code");String return_msg = (String)resp.get("return_msg");String result_code = (String)resp.get("result_code");String err_code = (String)resp.get("err_code");String err_code_des = (String)resp.get("err_code_des");String trade_state = (String)resp.get("trade_state");String trade_state_desc = (String)resp.get("trade_state_desc");if("SUCCESS".equals(return_code)){//微信返回狀態(tài)碼為成功if("SUCCESS".equals(result_code)){//業(yè)務(wù)結(jié)果狀態(tài)碼為成功if("SUCCESS".equals(trade_state)){//交易狀態(tài)為成功return resp;}else if("USERPAYING".equals(trade_state)){//支付中return resp;}else{//交易狀態(tài)為不是成功lg.info("***************支付平臺(tái)訂單ID:"+orderId+"查詢微信支付接口異常:trade_state="+trade_state+",trade_state_desc="+trade_state_desc);resp = null;return resp;}}else{//業(yè)務(wù)結(jié)果狀態(tài)碼為失敗lg.info("***************支付平臺(tái)訂單ID:"+orderId+"查詢微信支付接口異常:err_code="+err_code+",err_code_des="+err_code_des);resp = null;return resp;}}else{//微信返回狀態(tài)碼為失敗lg.info("***************支付平臺(tái)訂單ID:"+orderId+"查詢微信支付接口異常:"+err_code);resp = null;return resp;}}catch(Exception e){lg.info("***************支付平臺(tái)訂單ID:"+orderId+"查詢微信支付接口:"+e.getMessage());e.printStackTrace();resp = null;}//僅返回交易狀態(tài)trade_state是SUCCESS的值return resp;}到這里微信掃碼支付的流程已經(jīng)結(jié)束了。
第一次寫文章,有什么遺漏的地方歡迎大家在下面評(píng)論。
下面補(bǔ)充遺漏的工具類(代碼過長,可創(chuàng)建對(duì)應(yīng)類名的java文件,直接復(fù)制粘貼即可)
字符串工具類
import java.io.StringReader; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Map.Entry;/*** 字符串工具類** @author xiaoleilu*/ public class StrKit {public static final String SPACE = " ";public static final String DOT = ".";public static final String SLASH = "/";public static final String BACKSLASH = "\\";public static final String EMPTY = "";public static final String CRLF = "\r\n";public static final String NEWLINE = "\n";public static final String UNDERLINE = "_";public static final String COMMA = ",";public static final String HTML_NBSP = " ";public static final String HTML_AMP = "&";public static final String HTML_QUOTE = """;public static final String HTML_LT = "<";public static final String HTML_GT = ">";public static final String EMPTY_JSON = "{}";/*** 首字母變小寫*/public static String firstCharToLowerCase(String str) {char firstChar = str.charAt(0);if (firstChar >= 'A' && firstChar <= 'Z') {char[] arr = str.toCharArray();arr[0] += ('a' - 'A');return new String(arr);}return str;}/*** 首字母變大寫*/public static String firstCharToUpperCase(String str) {char firstChar = str.charAt(0);if (firstChar >= 'a' && firstChar <= 'z') {char[] arr = str.toCharArray();arr[0] -= ('a' - 'A');return new String(arr);}return str;}// ------------------------------------------------------------------------ Blank/*** 字符串是否為空白 空白的定義如下: <br>* 1、為null <br>* 2、為不可見字符(如空格)<br>* 3、""<br>** @param str 被檢測的字符串* @return 是否為空*/public static boolean isBlank(String str) {int length;if ((str == null) || ((length = str.length()) == 0)) {return true;}for (int i = 0; i < length; i++) {// 只要有一個(gè)非空字符即為非空字符串if (false == Character.isWhitespace(str.charAt(i))) {return false;}}return true;}/*** 字符串是否為非空白 空白的定義如下: <br>* 1、不為null <br>* 2、不為不可見字符(如空格)<br>* 3、不為""<br>** @param str 被檢測的字符串* @return 是否為非空*/public static boolean notBlank(String str) {return false == isBlank(str);}/*** 是否包含空字符串** @param strs 字符串列表* @return 是否包含空字符串*/public static boolean hasBlank(String... strs) {if (CollectionKit.isEmpty(strs)) {return true;}for (String str : strs) {if (isBlank(str)) {return true;}}return false;}/*** 給定所有字符串是否為空白** @param strs 字符串* @return 所有字符串是否為空白*/public static boolean isAllBlank(String... strs) {if (CollectionKit.isEmpty(strs)) {return true;}for (String str : strs) {if (notBlank(str)) {return false;}}return true;}// ------------------------------------------------------------------------ Empty/*** 字符串是否為空,空的定義如下 1、為null <br>* 2、為""<br>** @param str 被檢測的字符串* @return 是否為空*/public static boolean isEmpty(String str) {return str == null || str.length() == 0;}/*** 字符串是否為非空白 空白的定義如下: <br>* 1、不為null <br>* 2、不為""<br>** @param str 被檢測的字符串* @return 是否為非空*/public static boolean isNotEmpty(String str) {return false == isEmpty(str);}/*** 當(dāng)給定字符串為null時(shí),轉(zhuǎn)換為Empty** @param str 被轉(zhuǎn)換的字符串* @return 轉(zhuǎn)換后的字符串*/public static String nullToEmpty(String str) {return nullToDefault(str, EMPTY);}/*** 如果字符串是<code>null</code>,則返回指定默認(rèn)字符串,否則返回字符串本身。** <pre>* nullToDefault(null, "default") = "default"* nullToDefault("", "default") = ""* nullToDefault(" ", "default") = " "* nullToDefault("bat", "default") = "bat"* </pre>** @param str 要轉(zhuǎn)換的字符串* @param defaultStr 默認(rèn)字符串* @return 字符串本身或指定的默認(rèn)字符串*/public static String nullToDefault(String str, String defaultStr) {return (str == null) ? defaultStr : str;}/*** 當(dāng)給定字符串為空字符串時(shí),轉(zhuǎn)換為<code>null</code>** @param str 被轉(zhuǎn)換的字符串* @return 轉(zhuǎn)換后的字符串*/public static String emptyToNull(String str) {return isEmpty(str) ? null : str;}/*** 是否包含空字符串** @param strs 字符串列表* @return 是否包含空字符串*/public static boolean hasEmpty(String... strs) {if (CollectionKit.isEmpty(strs)) {return true;}for (String str : strs) {if (isEmpty(str)) {return true;}}return false;}/*** 是否全部為空字符串** @param strs 字符串列表* @return 是否全部為空字符串*/public static boolean isAllEmpty(String... strs) {if (CollectionKit.isEmpty(strs)) {return true;}for (String str : strs) {if (isNotEmpty(str)) {return false;}}return true;}// ------------------------------------------------------------------------ Trim/*** 除去字符串頭尾部的空白,如果字符串是<code>null</code>,依然返回<code>null</code>。** <p>* 注意,和<code>String.trim</code>不同,此方法使用<code>Character.isWhitespace</code> 來判定空白, 因而可以除去英文字符集之外的其它空白,如中文空格。** <pre>* trim(null) = null* trim("") = ""* trim(" ") = ""* trim("abc") = "abc"* trim(" abc ") = "abc"* </pre>** </p>** @param str 要處理的字符串* @return 除去空白的字符串,如果原字串為<code>null</code>,則返回<code>null</code>*/public static String trim(String str) {return (null == str) ? null : trim(str, 0);}/*** 給定字符串?dāng)?shù)組全部做去首尾空格** @param strs 字符串?dāng)?shù)組*/public static void trim(String[] strs) {if (null == strs) {return;}String str;for (int i = 0; i < strs.length; i++) {str = strs[i];if (null != str) {strs[i] = str.trim();}}}/*** 除去字符串頭部的空白,如果字符串是<code>null</code>,則返回<code>null</code>。** <p>* 注意,和<code>String.trim</code>不同,此方法使用<code>Character.isWhitespace</code> 來判定空白, 因而可以除去英文字符集之外的其它空白,如中文空格。** <pre>* trimStart(null) = null* trimStart("") = ""* trimStart("abc") = "abc"* trimStart(" abc") = "abc"* trimStart("abc ") = "abc "* trimStart(" abc ") = "abc "* </pre>** </p>** @param str 要處理的字符串* @return 除去空白的字符串,如果原字串為<code>null</code>或結(jié)果字符串為<code>""</code>,則返回 <code>null</code>*/public static String trimStart(String str) {return trim(str, -1);}/*** 除去字符串尾部的空白,如果字符串是<code>null</code>,則返回<code>null</code>。** <p>* 注意,和<code>String.trim</code>不同,此方法使用<code>Character.isWhitespace</code> 來判定空白, 因而可以除去英文字符集之外的其它空白,如中文空格。** <pre>* trimEnd(null) = null* trimEnd("") = ""* trimEnd("abc") = "abc"* trimEnd(" abc") = " abc"* trimEnd("abc ") = "abc"* trimEnd(" abc ") = " abc"* </pre>** </p>** @param str 要處理的字符串* @return 除去空白的字符串,如果原字串為<code>null</code>或結(jié)果字符串為<code>""</code>,則返回 <code>null</code>*/public static String trimEnd(String str) {return trim(str, 1);}/*** 除去字符串頭尾部的空白符,如果字符串是<code>null</code>,依然返回<code>null</code>。** @param str 要處理的字符串* @param mode <code>-1</code>表示trimStart,<code>0</code>表示trim全部, <code>1</code>表示trimEnd* @return 除去指定字符后的的字符串,如果原字串為<code>null</code>,則返回<code>null</code>*/public static String trim(String str, int mode) {if (str == null) {return null;}int length = str.length();int start = 0;int end = length;// 掃描字符串頭部if (mode <= 0) {while ((start < end) && (Character.isWhitespace(str.charAt(start)))) {start++;}}// 掃描字符串尾部if (mode >= 0) {while ((start < end) && (Character.isWhitespace(str.charAt(end - 1)))) {end--;}}if ((start > 0) || (end < length)) {return str.substring(start, end);}return str;}/*** 是否以指定字符串開頭** @param str 被監(jiān)測字符串* @param prefix 開頭字符串* @param isIgnoreCase 是否忽略大小寫* @return 是否以指定字符串開頭*/public static boolean startWith(String str, String prefix, boolean isIgnoreCase) {if (isIgnoreCase) {return str.toLowerCase().startsWith(prefix.toLowerCase());} else {return str.startsWith(prefix);}}/*** 是否以指定字符串結(jié)尾** @param str 被監(jiān)測字符串* @param suffix 結(jié)尾字符串* @param isIgnoreCase 是否忽略大小寫* @return 是否以指定字符串結(jié)尾*/public static boolean endWith(String str, String suffix, boolean isIgnoreCase) {if (isIgnoreCase) {return str.toLowerCase().endsWith(suffix.toLowerCase());} else {return str.endsWith(suffix);}}/*** 是否包含特定字符,忽略大小寫,如果給定兩個(gè)參數(shù)都為<code>null</code>,返回true** @param str 被檢測字符串* @param testStr 被測試是否包含的字符串* @return 是否包含*/public static boolean containsIgnoreCase(String str, String testStr) {if (null == str) {//如果被監(jiān)測字符串和return null == testStr;}return str.toLowerCase().contains(testStr.toLowerCase());}/*** 獲得set或get方法對(duì)應(yīng)的標(biāo)準(zhǔn)屬性名<br/>* 例如:setName 返回 name** @param getOrSetMethodName* @return 如果是set或get方法名,返回field, 否則null*/public static String getGeneralField(String getOrSetMethodName) {if (getOrSetMethodName.startsWith("get") || getOrSetMethodName.startsWith("set")) {return cutPreAndLowerFirst(getOrSetMethodName, 3);}return null;}/*** 生成set方法名<br/>* 例如:name 返回 setName** @param fieldName 屬性名* @return setXxx*/public static String genSetter(String fieldName) {return upperFirstAndAddPre(fieldName, "set");}/*** 生成get方法名** @param fieldName 屬性名* @return getXxx*/public static String genGetter(String fieldName) {return upperFirstAndAddPre(fieldName, "get");}/*** 去掉首部指定長度的字符串并將剩余字符串首字母小寫<br/>* 例如:str=setName, preLength=3 -> return name** @param str 被處理的字符串* @param preLength 去掉的長度* @return 處理后的字符串,不符合規(guī)范返回null*/public static String cutPreAndLowerFirst(String str, int preLength) {if (str == null) {return null;}if (str.length() > preLength) {char first = Character.toLowerCase(str.charAt(preLength));if (str.length() > preLength + 1) {return first + str.substring(preLength + 1);}return String.valueOf(first);}return null;}/*** 原字符串首字母大寫并在其首部添加指定字符串 例如:str=name, preString=get -> return getName** @param str 被處理的字符串* @param preString 添加的首部* @return 處理后的字符串*/public static String upperFirstAndAddPre(String str, String preString) {if (str == null || preString == null) {return null;}return preString + upperFirst(str);}/*** 大寫首字母<br>* 例如:str = name, return Name** @param str 字符串* @return 字符串*/public static String upperFirst(String str) {return Character.toUpperCase(str.charAt(0)) + str.substring(1);}/*** 小寫首字母<br>* 例如:str = Name, return name** @param str 字符串* @return 字符串*/public static String lowerFirst(String str) {if (isBlank(str)) {return str;}return Character.toLowerCase(str.charAt(0)) + str.substring(1);}/*** 去掉指定前綴** @param str 字符串* @param prefix 前綴* @return 切掉后的字符串,若前綴不是 preffix, 返回原字符串*/public static String removePrefix(String str, String prefix) {if (isEmpty(str) || isEmpty(prefix)) {return str;}if (str.startsWith(prefix)) {return str.substring(prefix.length());}return str;}/*** 忽略大小寫去掉指定前綴** @param str 字符串* @param prefix 前綴* @return 切掉后的字符串,若前綴不是 prefix, 返回原字符串*/public static String removePrefixIgnoreCase(String str, String prefix) {if (isEmpty(str) || isEmpty(prefix)) {return str;}if (str.toLowerCase().startsWith(prefix.toLowerCase())) {return str.substring(prefix.length());}return str;}/*** 去掉指定后綴** @param str 字符串* @param suffix 后綴* @return 切掉后的字符串,若后綴不是 suffix, 返回原字符串*/public static String removeSuffix(String str, String suffix) {if (isEmpty(str) || isEmpty(suffix)) {return str;}if (str.endsWith(suffix)) {return str.substring(0, str.length() - suffix.length());}return str;}/*** 獲得字符串對(duì)應(yīng)byte數(shù)組** @param str 字符串* @param charset 編碼,如果為<code>null</code>使用系統(tǒng)默認(rèn)編碼* @return bytes*/public static byte[] getBytes(String str, Charset charset) {if (null == str) {return null;}return null == charset ? str.getBytes() : str.getBytes(charset);}/*** 忽略大小寫去掉指定后綴** @param str 字符串* @param suffix 后綴* @return 切掉后的字符串,若后綴不是 suffix, 返回原字符串*/public static String removeSuffixIgnoreCase(String str, String suffix) {if (isEmpty(str) || isEmpty(suffix)) {return str;}if (str.toLowerCase().endsWith(suffix.toLowerCase())) {return str.substring(0, str.length() - suffix.length());}return str;}/*** 如果給定字符串不是以prefix開頭的,在開頭補(bǔ)充 prefix** @param str 字符串* @param prefix 前綴* @return 補(bǔ)充后的字符串*/public static String addPrefixIfNot(String str, String prefix) {if (isEmpty(str) || isEmpty(prefix)) {return str;}if (false == str.startsWith(prefix)) {str = prefix + str;}return str;}/*** 如果給定字符串不是以suffix結(jié)尾的,在尾部補(bǔ)充 suffix** @param str 字符串* @param suffix 后綴* @return 補(bǔ)充后的字符串*/public static String addSuffixIfNot(String str, String suffix) {if (isEmpty(str) || isEmpty(suffix)) {return str;}if (false == str.endsWith(suffix)) {str += suffix;}return str;}/*** 清理空白字符** @param str 被清理的字符串* @return 清理后的字符串*/public static String cleanBlank(String str) {if (str == null) {return null;}return str.replaceAll("\\s*", EMPTY);}/*** 切分字符串<br>* a#b#c -> [a,b,c] <br>* a##b#c -> [a,"",b,c]** @param str 被切分的字符串* @param separator 分隔符字符* @return 切分后的集合*/public static List<String> split(String str, char separator) {return split(str, separator, 0);}/*** 切分字符串** @param str 被切分的字符串* @param separator 分隔符字符* @param limit 限制分片數(shù)* @return 切分后的集合*/public static List<String> split(String str, char separator, int limit) {if (str == null) {return null;}List<String> list = new ArrayList<String>(limit == 0 ? 16 : limit);if (limit == 1) {list.add(str);return list;}boolean isNotEnd = true; // 未結(jié)束切分的標(biāo)志int strLen = str.length();StringBuilder sb = new StringBuilder(strLen);for (int i = 0; i < strLen; i++) {char c = str.charAt(i);if (isNotEnd && c == separator) {list.add(sb.toString());// 清空StringBuildersb.delete(0, sb.length());// 當(dāng)達(dá)到切分上限-1的量時(shí),將所剩字符全部作為最后一個(gè)串if (limit != 0 && list.size() == limit - 1) {isNotEnd = false;}} else {sb.append(c);}}list.add(sb.toString());// 加入尾串return list;}/*** 切分字符串<br>* from jodd** @param str 被切分的字符串* @param delimiter 分隔符* @return 字符串*/public static String[] split(String str, String delimiter) {if (str == null) {return null;}if (str.trim().length() == 0) {return new String[]{str};}int dellen = delimiter.length(); // del lengthint maxparts = (str.length() / dellen) + 2; // one more for the lastint[] positions = new int[maxparts];int i, j = 0;int count = 0;positions[0] = -dellen;while ((i = str.indexOf(delimiter, j)) != -1) {count++;positions[count] = i;j = i + dellen;}count++;positions[count] = str.length();String[] result = new String[count];for (i = 0; i < count; i++) {result[i] = str.substring(positions[i] + dellen, positions[i + 1]);}return result;}/*** 改進(jìn)JDK subString<br>* index從0開始計(jì)算,最后一個(gè)字符為-1<br>* 如果from和to位置一樣,返回 "" <br>* 如果from或to為負(fù)數(shù),則按照length從后向前數(shù)位置,如果絕對(duì)值大于字符串長度,則from歸到0,to歸到length<br>* 如果經(jīng)過修正的index中from大于to,則互換from和to* example: <br>* abcdefgh 2 3 -> c <br>* abcdefgh 2 -3 -> cde <br>** @param string String* @param fromIndex 開始的index(包括)* @param toIndex 結(jié)束的index(不包括)* @return 字串*/public static String sub(String string, int fromIndex, int toIndex) {int len = string.length();if (fromIndex < 0) {fromIndex = len + fromIndex;if (fromIndex < 0) {fromIndex = 0;}} else if (fromIndex >= len) {fromIndex = len - 1;}if (toIndex < 0) {toIndex = len + toIndex;if (toIndex < 0) {toIndex = len;}} else if (toIndex > len) {toIndex = len;}if (toIndex < fromIndex) {int tmp = fromIndex;fromIndex = toIndex;toIndex = tmp;}if (fromIndex == toIndex) {return EMPTY;}char[] strArray = string.toCharArray();char[] newStrArray = Arrays.copyOfRange(strArray, fromIndex, toIndex);return new String(newStrArray);}/*** 切割前部分** @param string 字符串* @param toIndex 切割到的位置(不包括)* @return 切割后的字符串*/public static String subPre(String string, int toIndex) {return sub(string, 0, toIndex);}/*** 切割后部分** @param string 字符串* @param fromIndex 切割開始的位置(包括)* @return 切割后的字符串*/public static String subSuf(String string, int fromIndex) {if (isEmpty(string)) {return null;}return sub(string, fromIndex, string.length());}/*** 給定字符串是否被字符包圍** @param str 字符串* @param prefix 前綴* @param suffix 后綴* @return 是否包圍,空串不包圍*/public static boolean isSurround(String str, String prefix, String suffix) {if (StrKit.isBlank(str)) {return false;}if (str.length() < (prefix.length() + suffix.length())) {return false;}return str.startsWith(prefix) && str.endsWith(suffix);}/*** 給定字符串是否被字符包圍** @param str 字符串* @param prefix 前綴* @param suffix 后綴* @return 是否包圍,空串不包圍*/public static boolean isSurround(String str, char prefix, char suffix) {if (StrKit.isBlank(str)) {return false;}if (str.length() < 2) {return false;}return str.charAt(0) == prefix && str.charAt(str.length() - 1) == suffix;}/*** 重復(fù)某個(gè)字符** @param c 被重復(fù)的字符* @param count 重復(fù)的數(shù)目* @return 重復(fù)字符字符串*/public static String repeat(char c, int count) {char[] result = new char[count];for (int i = 0; i < count; i++) {result[i] = c;}return new String(result);}/*** 重復(fù)某個(gè)字符串** @param str 被重復(fù)的字符* @param count 重復(fù)的數(shù)目* @return 重復(fù)字符字符串*/public static String repeat(String str, int count) {// 檢查final int len = str.length();final long longSize = (long) len * (long) count;final int size = (int) longSize;if (size != longSize) {throw new ArrayIndexOutOfBoundsException("Required String length is too large: " + longSize);}final char[] array = new char[size];str.getChars(0, len, array, 0);int n;for (n = len; n < size - n; n <<= 1) {// n <<= 1相當(dāng)于n *2System.arraycopy(array, 0, array, n, n);}System.arraycopy(array, 0, array, n, size - n);return new String(array);}/*** 比較兩個(gè)字符串(大小寫敏感)。** <pre>* equals(null, null) = true* equals(null, "abc") = false* equals("abc", null) = false* equals("abc", "abc") = true* equals("abc", "ABC") = false* </pre>** @param str1 要比較的字符串1* @param str2 要比較的字符串2* @return 如果兩個(gè)字符串相同,或者都是<code>null</code>,則返回<code>true</code>*/public static boolean equals(String str1, String str2) {if (str1 == null) {return str2 == null;}return str1.equals(str2);}/*** 比較兩個(gè)字符串(大小寫不敏感)。** <pre>* equalsIgnoreCase(null, null) = true* equalsIgnoreCase(null, "abc") = false* equalsIgnoreCase("abc", null) = false* equalsIgnoreCase("abc", "abc") = true* equalsIgnoreCase("abc", "ABC") = true* </pre>** @param str1 要比較的字符串1* @param str2 要比較的字符串2* @return 如果兩個(gè)字符串相同,或者都是<code>null</code>,則返回<code>true</code>*/public static boolean equalsIgnoreCase(String str1, String str2) {if (str1 == null) {return str2 == null;}return str1.equalsIgnoreCase(str2);}/*** 格式化文本, {} 表示占位符<br>* 例如:format("aaa {} ccc", "bbb") ----> aaa bbb ccc** @param template 文本模板,被替換的部分用 {} 表示* @param values 參數(shù)值* @return 格式化后的文本*/public static String format(String template, Object... values) {if (CollectionKit.isEmpty(values) || isBlank(template)) {return template;}final StringBuilder sb = new StringBuilder();final int length = template.length();int valueIndex = 0;char currentChar;for (int i = 0; i < length; i++) {if (valueIndex >= values.length) {sb.append(sub(template, i, length));break;}currentChar = template.charAt(i);if (currentChar == '{') {final char nextChar = template.charAt(++i);if (nextChar == '}') {sb.append(values[valueIndex++]);} else {sb.append('{').append(nextChar);}} else {sb.append(currentChar);}}return sb.toString();}/*** 格式化文本,使用 {varName} 占位<br>* map = {a: "aValue", b: "bValue"}* format("{a} and ", map) ----> aValue and bValue** @param template 文本模板,被替換的部分用 {key} 表示* @param map 參數(shù)值對(duì)* @return 格式化后的文本*/public static String format(String template, Map<?, ?> map) {if (null == map || map.isEmpty()) {return template;}for (Entry<?, ?> entry : map.entrySet()) {template = template.replace("{" + entry.getKey() + "}", entry.getValue().toString());}return template;}/*** 編碼字符串** @param str 字符串* @param charset 字符集,如果此字段為空,則解碼的結(jié)果取決于平臺(tái)* @return 編碼后的字節(jié)碼*/public static byte[] bytes(String str, String charset) {return bytes(str, isBlank(charset) ? Charset.defaultCharset() : Charset.forName(charset));}/*** 編碼字符串** @param str 字符串* @param charset 字符集,如果此字段為空,則解碼的結(jié)果取決于平臺(tái)* @return 編碼后的字節(jié)碼*/public static byte[] bytes(String str, Charset charset) {if (str == null) {return null;}if (null == charset) {return str.getBytes();}return str.getBytes(charset);}/*** 將byte數(shù)組轉(zhuǎn)為字符串** @param bytes byte數(shù)組* @param charset 字符集* @return 字符串*/public static String str(byte[] bytes, String charset) {return str(bytes, isBlank(charset) ? Charset.defaultCharset() : Charset.forName(charset));}/*** 解碼字節(jié)碼** @param data 字符串* @param charset 字符集,如果此字段為空,則解碼的結(jié)果取決于平臺(tái)* @return 解碼后的字符串*/public static String str(byte[] data, Charset charset) {if (data == null) {return null;}if (null == charset) {return new String(data);}return new String(data, charset);}/*** 將編碼的byteBuffer數(shù)據(jù)轉(zhuǎn)換為字符串** @param data 數(shù)據(jù)* @param charset 字符集,如果為空使用當(dāng)前系統(tǒng)字符集* @return 字符串*/public static String str(ByteBuffer data, String charset) {if (data == null) {return null;}return str(data, Charset.forName(charset));}/*** 將編碼的byteBuffer數(shù)據(jù)轉(zhuǎn)換為字符串** @param data 數(shù)據(jù)* @param charset 字符集,如果為空使用當(dāng)前系統(tǒng)字符集* @return 字符串*/public static String str(ByteBuffer data, Charset charset) {if (null == charset) {charset = Charset.defaultCharset();}return charset.decode(data).toString();}/*** 字符串轉(zhuǎn)換為byteBuffer** @param str 字符串* @param charset 編碼* @return byteBuffer*/public static ByteBuffer byteBuffer(String str, String charset) {return ByteBuffer.wrap(StrKit.bytes(str, charset));}/*** 以 conjunction 為分隔符將多個(gè)對(duì)象轉(zhuǎn)換為字符串** @param conjunction 分隔符* @param objs 數(shù)組* @return 連接后的字符串*/public static String join(String conjunction, Object... objs) {StringBuilder sb = new StringBuilder();boolean isFirst = true;for (Object item : objs) {if (isFirst) {isFirst = false;} else {sb.append(conjunction);}sb.append(item);}return sb.toString();}/*** 將駝峰式命名的字符串轉(zhuǎn)換為下劃線方式。如果轉(zhuǎn)換前的駝峰式命名的字符串為空,則返回空字符串。</br>* 例如:HelloWorld->hello_world** @param camelCaseStr 轉(zhuǎn)換前的駝峰式命名的字符串* @return 轉(zhuǎn)換后下劃線大寫方式命名的字符串*/public static String toUnderlineCase(String camelCaseStr) {if (camelCaseStr == null) {return null;}final int length = camelCaseStr.length();StringBuilder sb = new StringBuilder();char c;boolean isPreUpperCase = false;for (int i = 0; i < length; i++) {c = camelCaseStr.charAt(i);boolean isNextUpperCase = true;if (i < (length - 1)) {isNextUpperCase = Character.isUpperCase(camelCaseStr.charAt(i + 1));}if (Character.isUpperCase(c)) {if (!isPreUpperCase || !isNextUpperCase) {if (i > 0) sb.append(UNDERLINE);}isPreUpperCase = true;} else {isPreUpperCase = false;}sb.append(Character.toLowerCase(c));}return sb.toString();}/*** 將下劃線方式命名的字符串轉(zhuǎn)換為駝峰式。如果轉(zhuǎn)換前的下劃線大寫方式命名的字符串為空,則返回空字符串。</br>* 例如:hello_world->HelloWorld** @param name 轉(zhuǎn)換前的下劃線大寫方式命名的字符串* @return 轉(zhuǎn)換后的駝峰式命名的字符串*/public static String toCamelCase(String name) {if (name == null) {return null;}if (name.contains(UNDERLINE)) {name = name.toLowerCase();StringBuilder sb = new StringBuilder(name.length());boolean upperCase = false;for (int i = 0; i < name.length(); i++) {char c = name.charAt(i);if (c == '_') {upperCase = true;} else if (upperCase) {sb.append(Character.toUpperCase(c));upperCase = false;} else {sb.append(c);}}return sb.toString();} elsereturn name;}/*** 包裝指定字符串** @param str 被包裝的字符串* @param prefix 前綴* @param suffix 后綴* @return 包裝后的字符串*/public static String wrap(String str, String prefix, String suffix) {return format("{}{}{}", prefix, str, suffix);}/*** 指定字符串是否被包裝** @param str 字符串* @param prefix 前綴* @param suffix 后綴* @return 是否被包裝*/public static boolean isWrap(String str, String prefix, String suffix) {return str.startsWith(prefix) && str.endsWith(suffix);}/*** 指定字符串是否被同一字符包裝(前后都有這些字符串)** @param str 字符串* @param wrapper 包裝字符串* @return 是否被包裝*/public static boolean isWrap(String str, String wrapper) {return isWrap(str, wrapper, wrapper);}/*** 指定字符串是否被同一字符包裝(前后都有這些字符串)** @param str 字符串* @param wrapper 包裝字符* @return 是否被包裝*/public static boolean isWrap(String str, char wrapper) {return isWrap(str, wrapper, wrapper);}/*** 指定字符串是否被包裝** @param str 字符串* @param prefixChar 前綴* @param suffixChar 后綴* @return 是否被包裝*/public static boolean isWrap(String str, char prefixChar, char suffixChar) {return str.charAt(0) == prefixChar && str.charAt(str.length() - 1) == suffixChar;}/*** 補(bǔ)充字符串以滿足最小長度 StrUtil.padPre("1", 3, '0');//"001"** @param str 字符串* @param minLength 最小長度* @param padChar 補(bǔ)充的字符* @return 補(bǔ)充后的字符串*/public static String padPre(String str, int minLength, char padChar) {if (str.length() >= minLength) {return str;}StringBuilder sb = new StringBuilder(minLength);for (int i = str.length(); i < minLength; i++) {sb.append(padChar);}sb.append(str);return sb.toString();}/*** 補(bǔ)充字符串以滿足最小長度 StrUtil.padEnd("1", 3, '0');//"100"** @param str 字符串* @param minLength 最小長度* @param padChar 補(bǔ)充的字符* @return 補(bǔ)充后的字符串*/public static String padEnd(String str, int minLength, char padChar) {if (str.length() >= minLength) {return str;}StringBuilder sb = new StringBuilder(minLength);sb.append(str);for (int i = str.length(); i < minLength; i++) {sb.append(padChar);}return sb.toString();}/*** 創(chuàng)建StringBuilder對(duì)象** @return StringBuilder對(duì)象*/public static StringBuilder builder() {return new StringBuilder();}/*** 創(chuàng)建StringBuilder對(duì)象** @return StringBuilder對(duì)象*/public static StringBuilder builder(int capacity) {return new StringBuilder(capacity);}/*** 創(chuàng)建StringBuilder對(duì)象** @return StringBuilder對(duì)象*/public static StringBuilder builder(String... strs) {final StringBuilder sb = new StringBuilder();for (String str : strs) {sb.append(str);}return sb;}/*** 獲得StringReader** @param str 字符串* @return StringReader*/public static StringReader getReader(String str) {return new StringReader(str);}/*** 獲得StringWriter** @return StringWriter*/public static StringWriter getWriter() {return new StringWriter();}/*** 編碼字符串** @param str 字符串* @param charset 字符集,如果此字段為空,則解碼的結(jié)果取決于平臺(tái)* @return 編碼后的字節(jié)碼*/public static byte[] encode(String str, String charset) {if (str == null) {return null;}if (isBlank(charset)) {return str.getBytes();}try {return str.getBytes(charset);} catch (UnsupportedEncodingException e) {throw new RuntimeException(format("Charset [{}] unsupported!", charset));}}/*** 解碼字節(jié)碼** @param data 字符串* @param charset 字符集,如果此字段為空,則解碼的結(jié)果取決于平臺(tái)* @return 解碼后的字符串*/public static String decode(byte[] data, String charset) {if (data == null) {return null;}if (isBlank(charset)) {return new String(data);}try {return new String(data, charset);} catch (UnsupportedEncodingException e) {throw new RuntimeException(format("Charset [{}] unsupported!", charset));}} }XML工具類
import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map;import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.input.SAXBuilder;public class XMLUtil {/*** 解析xml,返回第一級(jí)元素鍵值對(duì)。如果第一級(jí)元素有子節(jié)點(diǎn),則此節(jié)點(diǎn)的值是子節(jié)點(diǎn)的xml數(shù)據(jù)。* @param strxml* @return* @throws JDOMException* @throws IOException*/public static Map doXMLParse(String strxml) throws JDOMException, IOException {strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");if(null == strxml || "".equals(strxml)) {return null;}Map m = new HashMap();InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));SAXBuilder builder = new SAXBuilder();Document doc = builder.build(in);Element root = doc.getRootElement();List list = root.getChildren();Iterator it = list.iterator();while(it.hasNext()) {Element e = (Element) it.next();String k = e.getName();String v = "";List children = e.getChildren();if(children.isEmpty()) {v = e.getTextNormalize();} else {v = XMLUtil.getChildrenText(children);}m.put(k, v);}//關(guān)閉流in.close();return m;}/*** 獲取子結(jié)點(diǎn)的xml* @param children* @return String*/public static String getChildrenText(List children) {StringBuffer sb = new StringBuffer();if(!children.isEmpty()) {Iterator it = children.iterator();while(it.hasNext()) {Element e = (Element) it.next();String name = e.getName();String value = e.getTextNormalize();List list = e.getChildren();sb.append("<" + name + ">");if(!list.isEmpty()) {sb.append(XMLUtil.getChildrenText(list));}sb.append(value);sb.append("</" + name + ">");}}return sb.toString();}}MD5加密工具
import com.jgntech.medicine.core.kit.support.StrKit;import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException;/*** MD5加密類(封裝jdk自帶的md5加密方法)** @author fengshuonan* @date 2016年12月2日 下午4:14:22*/ public class MD5Util {public static String encrypt(String source) throws UnsupportedEncodingException {if (StrKit.isNotEmpty(source)) {return encodeMd5(source.getBytes("UTF-8"));}return null;}private static String encodeMd5(byte[] source) {try {return encodeHex(MessageDigest.getInstance("MD5").digest(source));} catch (NoSuchAlgorithmException e) {throw new IllegalStateException(e.getMessage(), e);}}private static String encodeHex(byte[] bytes) {StringBuffer buffer = new StringBuffer(bytes.length * 2);for (int i = 0; i < bytes.length; i++) {if (((int) bytes[i] & 0xff) < 0x10)buffer.append("0");buffer.append(Long.toString((int) bytes[i] & 0xff, 16));}return buffer.toString();}public static String MD5Encode(String origin, String charsetname) {String resultString = null;try {resultString = new String(origin);MessageDigest md = MessageDigest.getInstance("MD5");if (charsetname == null || "".equals(charsetname))resultString = byteArrayToHexString(md.digest(resultString.getBytes()));elseresultString = byteArrayToHexString(md.digest(resultString.getBytes(charsetname)));} catch (Exception exception) {}return resultString;}private static String byteArrayToHexString(byte b[]) {StringBuffer resultSb = new StringBuffer();for (int i = 0; i < b.length; i++)resultSb.append(byteToHexString(b[i]));return resultSb.toString();}private static String byteToHexString(byte b) {int n = b;if (n < 0)n += 256;int d1 = n / 16;int d2 = n % 16;return hexDigits[d1] + hexDigits[d2];}private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5","6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };public static void main(String[] args) throws UnsupportedEncodingException {System.out.println(encrypt("123456"));} }http請(qǐng)求工具類
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.URL; import java.net.URLConnection;import org.apache.log4j.Logger;/*** http請(qǐng)求工具類* @author chenp**/ public class HttpUtils {private final static int CONNECT_TIMEOUT = 5000; // in milliseconds 連接超時(shí)的時(shí)間private final static String DEFAULT_ENCODING = "UTF-8"; //字符串編碼private static Logger lg=Logger.getLogger(HttpUtils.class);public static String postData(String urlStr, String data){return postData(urlStr, data, null);}/*** post數(shù)據(jù)請(qǐng)求* @param urlStr* @param data* @param contentType* @return*/public static String postData(String urlStr, String data, String contentType){BufferedReader reader = null;try {URL url = new URL(urlStr);URLConnection conn = url.openConnection();conn.setDoOutput(true);conn.setConnectTimeout(CONNECT_TIMEOUT);conn.setReadTimeout(CONNECT_TIMEOUT);if(contentType != null)conn.setRequestProperty("content-type", contentType);OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), DEFAULT_ENCODING);if(data == null)data = "";writer.write(data);writer.flush();writer.close();reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), DEFAULT_ENCODING));StringBuilder sb = new StringBuilder();String line = null;while ((line = reader.readLine()) != null) {sb.append(line);sb.append("\r\n");}return sb.toString();} catch (IOException e) {lg.info("Error connecting to " + urlStr + ": " + e.getMessage());} finally {try {if (reader != null)reader.close();} catch (IOException e) {}}return null;} }總結(jié)
以上是生活随笔為你收集整理的Java PC端微信、支付宝扫码支付(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 当当网图书项目
- 下一篇: linux搭建sftp服务器