Java PC端微信、支付宝扫码支付(一)

Java PC端微信、支付宝扫码支付(一)

前端时间写的项目用到了微信和支付宝的扫码支付,因为是第一次写,网上关于支付的资料也不多,所以用的时间比较长,趁现在工作不是太忙的时候对以前的工作总结一下。

一、微信
先附上微信的支付文档地址 https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_3
关于扫码支付微信给出了两种模式,具体官方文档都有说明,这里用的是模式二。

先说一下我在写的时候遇到的一个问题:在最开始写好支付的时候,测试也测试成功了,但是前端就是接收不到支付成功的通知,最后看了下官方文档,发现在支付回调里面支付成功的通知只是通知给支付的用户,也就是你用微信支付成功后会跳转到支付成功的界面,但是我们的前端却是收不到通知的。关于这个问题微信官方也给出了相应的对策,就是在用户发起支付请求的时候前端去轮询微信的查询订单的接口,每个五秒或者三秒一次,直到收到支付成功的通知或者交易关闭才结束。这时候你可能就会想到这样做会不会对我们服务端的压力过大,但是这也是没办法的事情,毕竟我们调的事第三方的接口。我当时也想到了这个问题,然后我在京东的下单页面看了一下人家的请求,发现京东也是通过轮询去查询订单状态,以此来判定订单是否支付成功。
既然大佬都这样做了想必也是最好的办法了。

在这里插入图片描述
京东是大概每隔五秒去查询以此订单状态。

下面是具体的代码

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

	@Override
    public Result wxPay(WeChatParams ps) throws Exception {
        TbPaymentAmount tbPaymentAmount = new TbPaymentAmount().selectById(ps.getAmountId());

        String numberCode = getNumberCode();

        Long doctorId = Login.userId();

        ps.setBody("服务充值");
        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();

        //在这里生成订单信息,此时订单是未支付状态

        String urlCode = WeixinPay.getCodeUrl(ps);

        Map<String,Object> maps=new HashMap<>();
        maps.put("urlCode",urlCode);
        maps.put("orderNo",numberCode);

        return Result.success(maps);
    }

上面是用户发起支付请求,生成二维码链接,然后返回给前端,前端有工具可以直接生成二维码展示在页面,在这里给前端返回了订单号,方便前端用来查询支付的交易状态,这个在后面会说到。在这里注意微信支付的支付金额是以分为单位的,就是在这里ps.setAmount()传入的金额要把你的实际金额乘以100。支付宝是没有这个问题的。

下面是支付用到的工具

	public static Logger lg=Logger.getLogger(WeixinPay.class);

	/**
     * 获取微信支付的二维码地址
     * @return
     * @author chenp
     * @throws Exception
     */
    public static String getCodeUrl(WeChatParams ps) throws Exception {
        /**
         * 账号信息
         */
        String appid = WeChatConfig.APPID;//微信服务号的appid
        String mch_id = WeChatConfig.MCHID; //微信支付商户号
        String key = WeChatConfig.APIKEY; // 微信支付的API密钥
        String notify_url = WeChatConfig.WECHAT_NOTIFY_URL_PC;//回调地址【注意,这里必须要使用外网的地址】
        String ufdoder_url=WeChatConfig.UFDODER_URL;//微信下单API地址
        String trade_type = "NATIVE"; //类型【网页扫码支付】

        /**
         * 时间字符串
         */
        String currTime = PayForUtil.getCurrTime();
        String strTime = currTime.substring(8, currTime.length());
        String strRandom = PayForUtil.buildRandom(4) + "";
        String nonce_str = strTime + strRandom;

        //修改到期时间
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date());//设置起始时间
        cal.add(Calendar.MINUTE, 1);//增加一分钟
        DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
        String format = df.format(cal.getTime());
        System.out.println(format);

        /**
         * 参数封装
         */
        SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();
        packageParams.put("appid", appid);
        packageParams.put("mch_id", mch_id);
        packageParams.put("nonce_str", nonce_str);//随机字符串
        packageParams.put("body", ps.body);//支付的商品名称
        packageParams.put("out_trade_no", ps.orderNo);//商户订单号【备注:每次发起请求都需要随机的字符串,否则失败。】
        packageParams.put("total_fee", ps.amount);//支付金额
        packageParams.put("spbill_create_ip", PayForUtil.localIp());//客户端主机
        packageParams.put("notify_url", notify_url);
        packageParams.put("trade_type", trade_type);
        packageParams.put("attach", ps.attach);//额外的参数【业务类型+会员ID+支付类型】
        packageParams.put("time_expire", format);//额外的参数【业务类型+会员ID+支付类型】

        String sign = PayForUtil.createSign("UTF-8", packageParams,key);  //获取签名
        packageParams.put("sign", sign);

        String requestXML = PayForUtil.getRequestXml(packageParams);//将请求参数转换成String类型
        lg.info("微信支付请求参数的报文"+requestXML);
        String resXml = HttpUtils.postData(ufdoder_url,requestXML);  //解析请求之后的xml参数并且转换成String类型
        Map map = XMLUtil.doXMLParse(resXml);
        lg.info("微信支付响应参数的报文"+resXml);
        String urlCode = (String) map.get("code_url");

        return urlCode;
    }
/**
 * 微信支付需要的一些参数
 * @author chenp
 *
 */
@Data//此注解代替了set、get方法,如果不想用这个注解也可以自己写set、get方法。
public class WeChatParams {
    @ApiModelProperty(value = "订单金额",hidden = true)
    public String amount;//订单金额【备注:以分为单位】
    @ApiModelProperty(value = "商品名称",hidden = true)
    public String body;//商品名称
    @ApiModelProperty(value = "商户订单号",hidden = true)
    public String orderNo;//商户订单号
    @ApiModelProperty(value = "附加参数",hidden = true)
    public String attach;//附加参数
    @ApiModelProperty(value = "医生ID",hidden = true)
    public String doctorId;//会员ID
    @ApiModelProperty("缴费金额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);

    /**
     * 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
     * @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
     *            请求参数
     * @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:将请求参数转换为xml格式的string
     * @param parameters
     *            请求参数
     * @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();
    }

    /**
     * 取出一个指定长度大小的随机正整数.
     *
     * @param length
     *            int 设定所取出随机数的长度。length小于11
     * @return int 返回生成的随机数。
     */
    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));
    }

    /**
     * 获取当前时间 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;
    }
    /**
     * 获取本机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("获取本机Ip失败:异常信息:"+e.getMessage());
        }
        return ip;
    }
}

package com.jgntech.medicine.core.config;

/**
 * Created by hcs on 2019/4/30 13:53
 */
public class WeChatConfig {

    /**
     * 微信服务号APPID
     */
    public static String APPID="";
    /**
     * 微信支付的商户号
     */
    public static String MCHID="";
    /**
     * 微信支付的API密钥
     */
    public static String APIKEY="";
    /**
     * 微信支付成功之后的回调地址【注意:当前回调地址必须是公网能够访问的地址】
     */
    public static String WECHAT_NOTIFY_URL_PC="";
    /**
     * 微信统一下单API地址
     */
    public static String UFDODER_URL="https://api.mch.weixin.qq.com/pay/unifiedorder";
    /**
     * true为使用真实金额支付,false为使用测试金额支付(1分)
     */
    public static String WXPAY="true";

}

上面就是生成支付二维码的代码了

下面就是支付回调,这个回调地址是在WeChatConfig 里面配置的,记住一定是外网可以访问的地址,如果没有服务器可以去百度内网穿透工具,我在这里用的是花生壳。

1.Controller层

	@ApiOperation("微信支付回调")
    @PostMapping("/wxNotify")
    public void wxNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
        tbPaymentRecordService.wechatNotifyUrlPc(request, response);
    }

2.Service层

	/**
     * 微信支付回调
     * @param request
     * @param response
     * @throws Exception
     */
    void wechatNotifyUrlPc(HttpServletRequest request, HttpServletResponse response) throws Exception;

3.Impl

	@Override
    public void wechatNotifyUrlPc(HttpServletRequest request, HttpServletResponse response) throws Exception {
        //读取参数
        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成map
        Map<String, String> m = new HashMap<String, String>();
        m = XMLUtil.doXMLParse(sb.toString());

        //过滤空 设置 TreeMap
        SortedMap<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; // key

        lg.info("微信支付返回回来的参数:" + packageParams);
        //判断签名是否正确
        if (PayForUtil.isTenpaySign("UTF-8", packageParams, key)) {
            //------------------------------
            //处理业务开始
            //------------------------------
            String resXml = "";
            //商户订单号
            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"))) {

                    // 这里是支付成功
                    //执行自己的业务逻辑开始(修改订单状态)
                    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");//是否关注公众号

                    //附加参数【商标申请_0bda32824db44d6f9611f1047829fa3b_15460】--【业务类型_会员ID_订单号】
                    String attach = (String) packageParams.get("attach");

                    //付款金额【以分为单位】
                    String total_fee = (String) packageParams.get("total_fee");
                    //微信生成的交易订单号
                    String transaction_id = (String) packageParams.get("transaction_id");//微信支付订单号
                    //支付完成时间
                    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("额外参数_attach:" + attach);
                    lg.info("time_end:" + time_end);

                    //执行自己的业务逻辑结束
                    lg.info("支付成功");
                    //通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了.
                    resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                            + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";

                } else {
                    lg.info("支付失败,错误信息:" + packageParams.get("err_code"));
                    resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                            + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";
                }
            } else {
                lg.info("无需重复支付");
                resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                        + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
            }
            //------------------------------
            //处理业务完毕
            //------------------------------
            BufferedOutputStream out = new BufferedOutputStream(
                    response.getOutputStream());
            out.write(resXml.getBytes());
            out.flush();
            out.close();
        } else {
            lg.info("通知签名验证失败");
        }
    }

在这里支付成功会给微信一个"SUCCESS",但是我们的前端却是收不到这个success的,这就需要我们写一个查询接口了,具体的参数和返回值信息可以查看微信的api文档 https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_2

查询接口跟下单接口差不多,可以写在WeixinPay这个类里面。
话不多说,直接上代码
1.Controller层

	//微信支付订单查询
    @ApiOperation("微信支付订单查询")
    @PostMapping("/wxPayExample")
    public Result wxPayExample(HttpServletRequest request,String  out_trade_no) throws Exception {
        return tbPaymentRecordService.wxPayExample(request, out_trade_no);
    }

在这里需要前端把下单接口返回的订单号传过来,在这里我们是用这个订单号查询订单的支付状态,不过还可以用微信的订单号(transaction_id)去查,但是据我了解这个订单号是在用户支付成功,也就是回调里面生成的订单号,如果真是这样的话在生成订单的时候我们就拿不到这个订单号了。

2.Service层

	/**
     * 微信支付订单查询
     * @return
     * @throws Exception
     */
    Result wxPayExample(HttpServletRequest request,String  out_trade_no) throws Exception;

3.Impl

	@Override
    public Result wxPayExample(HttpServletRequest request,String  out_trade_no) throws Exception {
        Map<String, String> resultMap = new HashMap();
        lg.info("*************************调用支付查询 start*************************");

        if( out_trade_no == null || out_trade_no.trim().equals("")){
            ThrowsKit.error(BizExceptionEnum.VALIDA_ERROR.msg("订单号为空"));
        }

        //查询微信支付状态
        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定时器查询微信订单结果为=="+map);
            if(map==null||map.isEmpty()){
                ThrowsKit.error(BizExceptionEnum.VALIDA_ERROR.msg("查询支付状态失败"));
            }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("查询支付状态失败"));
        }
        return Result.success(resultMap);
    }

4.WeixinPay类

	/**
     * 调用微信支付查询接口,返回支付信息
     * @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为测试环境
        Map<String, String> data = new HashMap<String, String>();
        data.put("out_trade_no", orderId);//订单号
        //data.put("transaction_id", orderId);//微信支付单号
        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)){//微信返回状态码为成功
                if("SUCCESS".equals(result_code)){//业务结果状态码为成功
                    if("SUCCESS".equals(trade_state)){//交易状态为成功
                        return resp;
                    }else if("USERPAYING".equals(trade_state)){
                        //支付中
                        return resp;
                    }
                    else{
                        //交易状态为不是成功
                        lg.info("***************支付平台订单ID:"+orderId+"查询微信支付接口异常:trade_state="+trade_state+",trade_state_desc="+trade_state_desc);
                        resp = null;
                        return resp;
                    }
                }
                else{
                    //业务结果状态码为失败
                    lg.info("***************支付平台订单ID:"+orderId+"查询微信支付接口异常:err_code="+err_code+",err_code_des="+err_code_des);
                    resp = null;
                    return resp;
                }
            }
            else{
                //微信返回状态码为失败
                lg.info("***************支付平台订单ID:"+orderId+"查询微信支付接口异常:"+err_code);
                resp = null;
                return resp;
            }

        }
        catch(Exception e){
            lg.info("***************支付平台订单ID:"+orderId+"查询微信支付接口:"+e.getMessage());
            e.printStackTrace();
            resp = null;
        }
        //仅返回交易状态trade_state是SUCCESS的值
        return resp;
    }

到这里微信扫码支付的流程已经结束了。
第一次写文章,有什么遗漏的地方欢迎大家在下面评论。

下面补充遗漏的工具类(代码过长,可创建对应类名的java文件,直接复制粘贴即可)

字符串工具类

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 = "&nbsp;";
    public static final String HTML_AMP = "&amp";
    public static final String HTML_QUOTE = "&quot;";
    public static final String HTML_LT = "&lt;";
    public static final String HTML_GT = "&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++) {
            // 只要有一个非空字符即为非空字符串
            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);
    }

    /**
     * 当给定字符串为null时,转换为Empty
     *
     * @param str 被转换的字符串
     * @return 转换后的字符串
     */
    public static String nullToEmpty(String str) {
        return nullToDefault(str, EMPTY);
    }

    /**
     * 如果字符串是<code>null</code>,则返回指定默认字符串,否则返回字符串本身。
     *
     * <pre>
     * nullToDefault(null, &quot;default&quot;)  = &quot;default&quot;
     * nullToDefault(&quot;&quot;, &quot;default&quot;)    = &quot;&quot;
     * nullToDefault(&quot;  &quot;, &quot;default&quot;)  = &quot;  &quot;
     * nullToDefault(&quot;bat&quot;, &quot;default&quot;) = &quot;bat&quot;
     * </pre>
     *
     * @param str        要转换的字符串
     * @param defaultStr 默认字符串
     * @return 字符串本身或指定的默认字符串
     */
    public static String nullToDefault(String str, String defaultStr) {
        return (str == null) ? defaultStr : str;
    }

    /**
     * 当给定字符串为空字符串时,转换为<code>null</code>
     *
     * @param str 被转换的字符串
     * @return 转换后的字符串
     */
    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(&quot;&quot;)            = &quot;&quot;
     * trim(&quot;     &quot;)       = &quot;&quot;
     * trim(&quot;abc&quot;)         = &quot;abc&quot;
     * trim(&quot;    abc    &quot;) = &quot;abc&quot;
     * </pre>
     *
     * </p>
     *
     * @param str 要处理的字符串
     * @return 除去空白的字符串,如果原字串为<code>null</code>,则返回<code>null</code>
     */
    public static String trim(String str) {
        return (null == str) ? null : trim(str, 0);
    }

    /**
     * 给定字符串数组全部做去首尾空格
     *
     * @param strs 字符串数组
     */
    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(&quot;&quot;)           = &quot;&quot;
     * trimStart(&quot;abc&quot;)        = &quot;abc&quot;
     * trimStart(&quot;  abc&quot;)      = &quot;abc&quot;
     * trimStart(&quot;abc  &quot;)      = &quot;abc  &quot;
     * trimStart(&quot; abc &quot;)      = &quot;abc &quot;
     * </pre>
     *
     * </p>
     *
     * @param str 要处理的字符串
     * @return 除去空白的字符串,如果原字串为<code>null</code>或结果字符串为<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(&quot;&quot;)         = &quot;&quot;
     * trimEnd(&quot;abc&quot;)      = &quot;abc&quot;
     * trimEnd(&quot;  abc&quot;)    = &quot;  abc&quot;
     * trimEnd(&quot;abc  &quot;)    = &quot;abc&quot;
     * trimEnd(&quot; abc &quot;)    = &quot; abc&quot;
     * </pre>
     *
     * </p>
     *
     * @param str 要处理的字符串
     * @return 除去空白的字符串,如果原字串为<code>null</code>或结果字符串为<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          被监测字符串
     * @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);
        }
    }

    /**
     * 是否以指定字符串结尾
     *
     * @param str          被监测字符串
     * @param suffix       结尾字符串
     * @param isIgnoreCase 是否忽略大小写
     * @return 是否以指定字符串结尾
     */
    public static boolean endWith(String str, String suffix, boolean isIgnoreCase) {
        if (isIgnoreCase) {
            return str.toLowerCase().endsWith(suffix.toLowerCase());
        } else {
            return str.endsWith(suffix);
        }
    }

    /**
     * 是否包含特定字符,忽略大小写,如果给定两个参数都为<code>null</code>,返回true
     *
     * @param str     被检测字符串
     * @param testStr 被测试是否包含的字符串
     * @return 是否包含
     */
    public static boolean containsIgnoreCase(String str, String testStr) {
        if (null == str) {
            //如果被监测字符串和
            return null == testStr;
        }
        return str.toLowerCase().contains(testStr.toLowerCase());
    }

    /**
     * 获得set或get方法对应的标准属性名<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 处理后的字符串,不符合规范返回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;
    }

    /**
     * 获得字符串对应byte数组
     *
     * @param str     字符串
     * @param charset 编码,如果为<code>null</code>使用系统默认编码
     * @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开头的,在开头补充 prefix
     *
     * @param str    字符串
     * @param prefix 前缀
     * @return 补充后的字符串
     */
    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结尾的,在尾部补充 suffix
     *
     * @param str    字符串
     * @param suffix 后缀
     * @return 补充后的字符串
     */
    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     限制分片数
     * @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; // 未结束切分的标志
        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());
                // 清空StringBuilder
                sb.delete(0, sb.length());

                // 当达到切分上限-1的量时,将所剩字符全部作为最后一个串
                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 length
        int maxparts = (str.length() / dellen) + 2; // one more for the last
        int[] 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;
    }

    /**
     * 改进JDK subString<br>
     * index从0开始计算,最后一个字符为-1<br>
     * 如果from和to位置一样,返回 "" <br>
     * 如果from或to为负数,则按照length从后向前数位置,如果绝对值大于字符串长度,则from归到0,to归到length<br>
     * 如果经过修正的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   结束的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;
    }

    /**
     * 重复某个字符
     *
     * @param c     被重复的字符
     * @param count 重复的数目
     * @return 重复字符字符串
     */
    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);
    }

    /**
     * 重复某个字符串
     *
     * @param str   被重复的字符
     * @param count 重复的数目
     * @return 重复字符字符串
     */
    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相当于n *2
            System.arraycopy(array, 0, array, n, n);
        }
        System.arraycopy(array, 0, array, n, size - n);
        return new String(array);
    }

    /**
     * 比较两个字符串(大小写敏感)。
     *
     * <pre>
     * equals(null, null)   = true
     * equals(null, &quot;abc&quot;)  = false
     * equals(&quot;abc&quot;, null)  = false
     * equals(&quot;abc&quot;, &quot;abc&quot;) = true
     * equals(&quot;abc&quot;, &quot;ABC&quot;) = false
     * </pre>
     *
     * @param str1 要比较的字符串1
     * @param str2 要比较的字符串2
     * @return 如果两个字符串相同,或者都是<code>null</code>,则返回<code>true</code>
     */
    public static boolean equals(String str1, String str2) {
        if (str1 == null) {
            return str2 == null;
        }

        return str1.equals(str2);
    }

    /**
     * 比较两个字符串(大小写不敏感)。
     *
     * <pre>
     * equalsIgnoreCase(null, null)   = true
     * equalsIgnoreCase(null, &quot;abc&quot;)  = false
     * equalsIgnoreCase(&quot;abc&quot;, null)  = false
     * equalsIgnoreCase(&quot;abc&quot;, &quot;abc&quot;) = true
     * equalsIgnoreCase(&quot;abc&quot;, &quot;ABC&quot;) = true
     * </pre>
     *
     * @param str1 要比较的字符串1
     * @param str2 要比较的字符串2
     * @return 如果两个字符串相同,或者都是<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   参数值
     * @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 {b}", map)    ---->    aValue and bValue
     *
     * @param template 文本模板,被替换的部分用 {key} 表示
     * @param map      参数值对
     * @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 字符集,如果此字段为空,则解码的结果取决于平台
     * @return 编码后的字节码
     */
    public static byte[] bytes(String str, String charset) {
        return bytes(str, isBlank(charset) ? Charset.defaultCharset() : Charset.forName(charset));
    }

    /**
     * 编码字符串
     *
     * @param str     字符串
     * @param charset 字符集,如果此字段为空,则解码的结果取决于平台
     * @return 编码后的字节码
     */
    public static byte[] bytes(String str, Charset charset) {
        if (str == null) {
            return null;
        }

        if (null == charset) {
            return str.getBytes();
        }
        return str.getBytes(charset);
    }

    /**
     * 将byte数组转为字符串
     *
     * @param bytes   byte数组
     * @param charset 字符集
     * @return 字符串
     */
    public static String str(byte[] bytes, String charset) {
        return str(bytes, isBlank(charset) ? Charset.defaultCharset() : Charset.forName(charset));
    }

    /**
     * 解码字节码
     *
     * @param data    字符串
     * @param charset 字符集,如果此字段为空,则解码的结果取决于平台
     * @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数据转换为字符串
     *
     * @param data    数据
     * @param charset 字符集,如果为空使用当前系统字符集
     * @return 字符串
     */
    public static String str(ByteBuffer data, String charset) {
        if (data == null) {
            return null;
        }

        return str(data, Charset.forName(charset));
    }

    /**
     * 将编码的byteBuffer数据转换为字符串
     *
     * @param data    数据
     * @param charset 字符集,如果为空使用当前系统字符集
     * @return 字符串
     */
    public static String str(ByteBuffer data, Charset charset) {
        if (null == charset) {
            charset = Charset.defaultCharset();
        }
        return charset.decode(data).toString();
    }

    /**
     * 字符串转换为byteBuffer
     *
     * @param str     字符串
     * @param charset 编码
     * @return byteBuffer
     */
    public static ByteBuffer byteBuffer(String str, String charset) {
        return ByteBuffer.wrap(StrKit.bytes(str, charset));
    }

    /**
     * 以 conjunction 为分隔符将多个对象转换为字符串
     *
     * @param conjunction 分隔符
     * @param objs        数组
     * @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();
    }

    /**
     * 将驼峰式命名的字符串转换为下划线方式。如果转换前的驼峰式命名的字符串为空,则返回空字符串。</br>
     * 例如:HelloWorld->hello_world
     *
     * @param camelCaseStr 转换前的驼峰式命名的字符串
     * @return 转换后下划线大写方式命名的字符串
     */
    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();
    }

    /**
     * 将下划线方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。</br>
     * 例如:hello_world->HelloWorld
     *
     * @param name 转换前的下划线大写方式命名的字符串
     * @return 转换后的驼峰式命名的字符串
     */
    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();
        } else
            return 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;
    }

    /**
     * 补充字符串以满足最小长度 StrUtil.padPre("1", 3, '0');//"001"
     *
     * @param str       字符串
     * @param minLength 最小长度
     * @param padChar   补充的字符
     * @return 补充后的字符串
     */
    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();
    }

    /**
     * 补充字符串以满足最小长度 StrUtil.padEnd("1", 3, '0');//"100"
     *
     * @param str       字符串
     * @param minLength 最小长度
     * @param padChar   补充的字符
     * @return 补充后的字符串
     */
    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();
    }

    /**
     * 创建StringBuilder对象
     *
     * @return StringBuilder对象
     */
    public static StringBuilder builder() {
        return new StringBuilder();
    }

    /**
     * 创建StringBuilder对象
     *
     * @return StringBuilder对象
     */
    public static StringBuilder builder(int capacity) {
        return new StringBuilder(capacity);
    }

    /**
     * 创建StringBuilder对象
     *
     * @return StringBuilder对象
     */
    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 字符集,如果此字段为空,则解码的结果取决于平台
     * @return 编码后的字节码
     */
    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));
        }
    }

    /**
     * 解码字节码
     *
     * @param data    字符串
     * @param charset 字符集,如果此字段为空,则解码的结果取决于平台
     * @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,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
     * @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);
        }

        //关闭流
        in.close();

        return m;
    }

    /**
     * 获取子结点的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()));
            else
                resultString = 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请求工具类

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请求工具类
 * @author chenp
 *
 */
public class HttpUtils {
    private final static int CONNECT_TIMEOUT = 5000; // in milliseconds  连接超时的时间
    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数据请求
     * @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;
    }
}

  • 18
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 14
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值