Java 实现微信支付详细教程

2023-11-17

摘要:最近的一个项目中涉及到了支付业务,其中用到了微信支付和支付宝支付,在做的过程中也遇到些问题,所以现在总结梳理一下,分享给有需要的人,也为自己以后回顾留个思路。

一、微信支付接入准备工作:

首先,微信支付,只支持企业用户,个人用户是不能接入微信支付的,所以要想接入微信支付,首先需要有微信公众号,这个的企业才能申请。有了微信公众号,就能申请微信支付的相关内容,所以在准备开始写代码之前需要先把下面的这些参数申请好:公众账号ID、微信支付商户号、API密钥、AppSecret是APPID对应的接口密码、回调地址(回调必须保证外网能访问到此地址)、发起请求的电脑IP

二、微信支付流程说明:

有了上面提到的这些参数,那我们就可以接入微信支付了,下面我来看下微信支付的官方文档(https://pay.weixin.qq.com/wiki/doc/api/index.html)、访问该地址可以看到有多种支付方式可以选择,我们这里选择扫码支付的方式(https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1)

这里我们选择模式二,下面看下模式二的时序图,如下图:

模式二与模式一相比,流程更为简单,不依赖设置的回调支付URL。商户后台系统先调用微信支付的统一下单接口,微信后台系统返回链接参数code_url,商户后台系统将code_url值生成二维码图片,用户使用微信客户端扫码后发起支付。注意:code_url有效期为2小时,过期后扫码不能再发起支付。

在这里插入图片描述

三、微信支付所需Maven依赖

    <!--微信支付SDK-->
        <dependency>
            <groupId>com.github.wechatpay-apiv3</groupId>
            <artifactId>wechatpay-apache-httpclient</artifactId>
            <version>0.3.0</version>
        </dependency>

        <!-- json处理器:引入gson依赖 -->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.6</version>
        </dependency>

        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.12</version>
        </dependency>

        <!-- 二维码 -->
        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>core</artifactId>
            <version>3.3.3</version>
        </dependency>
        <!-- 生成二维码 -->
        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>javase</artifactId>
            <version>3.3.3</version>
        </dependency>

四、配置文件添加微信支付所需参数

# 微信支付相关参数
wxpay:
  # 商户号
  mch-id: xxxxxxx
  # 商户API证书序列号
  mch-serial-no: xxxxxxxxxx
  # 商户私钥文件
  # 注意:该文件放在项目根目录下
  private-key-path: ./apiclient_key.pem
  # APIv3密钥
  api-v3-key: xxxxxxxx
  # APPID
  appid: xxxxxxc27e0e7cxxx
  # 微信服务器地址
  domain: https://api.mch.weixin.qq.com
  # 接收结果通知地址
  # 注意:每次重新启动ngrok,都需要根据实际情况修改这个配置
  notify-domain: https://c7c1-240e-3b5-3015-be0-1bc-9bed-fca4-d09b.ngrok.io

五、微信支付下单代码实现

1.Controller层

 /**
     * native下单
     */
    @ApiOperation(value = "native 微信支付下单 返回Image")
    @GetMapping("/native")
    public BaseRes<String> nativePay(@RequestParam("packageId") Integer packageId) {
        return wxPayService.nativePay(packageId);
    }

  /**
     * JSAPI下单
     */
    @ApiOperation(value = "JSAPI微信支付下单")
    @GetMapping("/jsapi")
    public BaseRes<String> jsapiPay(@RequestParam("packageId") Integer packageId,@RequestParam("openId") String openId) {
        return wxPayService.jsapiPay(packageId,openId);
    }

注意:packageId是套餐Id,可根据情况修改

2.Service层

   BaseRes<String> nativePay(Integer packageId);
   
   BaseRes<String> jsapiPay(Integer packageId, String openId);

3.实现层


    /**
     * Mavicat下单
     * @return
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    @SneakyThrows
    public BaseRes<String> nativePay(Integer packageId){

        log.info("发起Navicat支付请求");

        HttpPost httpPost = new HttpPost(wxPayConfig.getDomain().concat(WxApiType.NATIVE_PAY.getType()));

        CloseableHttpResponse response = wxPayExecute(packageId, null, httpPost);

        try {
            String bodyAsString = EntityUtils.toString(response.getEntity());//响应体
            int statusCode = response.getStatusLine().getStatusCode();//响应状态码
            if (statusCode == 200) { //处理成功
                log.info("成功, 返回结果 = " + bodyAsString);
            } else if (statusCode == 204) { //处理成功,无返回Body
                log.info("成功");
            } else {
                log.info("Native下单失败,响应码 = " + statusCode + ",返回结果 = " + bodyAsString);
                throw new IOException("request failed");
            }

            Gson gson = new Gson();

            //响应结果
            Map<String, String> resultMap = gson.fromJson(bodyAsString, HashMap.class);
            //二维码
            String codeUrl = resultMap.get("code_url");

            return new BaseRes<>(codeUrl,ServiceCode.SUCCESS);

            //生成二维码
//            WxPayUtil.makeQRCode(codeUrl);

        } finally {
            response.close();
        }
    }

  /**
     * JSAPI下单
     * @return
     */

    @Override
    @SneakyThrows
    public BaseRes<String> jsapiPay(Integer packageId, String openId) {

        log.info("发起Navicat支付请求");

        HttpPost httpPost = new HttpPost(wxPayConfig.getDomain().concat(WxApiType.JSAPI_PAY.getType()));

        CloseableHttpResponse response = wxPayExecute(packageId, openId, httpPost);

        try {
            String bodyAsString = EntityUtils.toString(response.getEntity());//响应体
            int statusCode = response.getStatusLine().getStatusCode();//响应状态码
            if (statusCode == 200) { //处理成功
                log.info("成功, 返回结果 = " + bodyAsString);
            } else if (statusCode == 204) { //处理成功,无返回Body
                log.info("成功");
            } else {
                log.info("JSAPI下单失败,响应码 = " + statusCode + ",返回结果 = " + bodyAsString);
                throw new IOException("request failed");
            }

            Gson gson = new Gson();

            //响应结果
            Map<String, String> resultMap = gson.fromJson(bodyAsString, HashMap.class);


            String prepayId = resultMap.get("prepay_id");

            return new BaseRes<>(prepayId,ServiceCode.SUCCESS);

        } finally {
            response.close();
        }
    }


 // 封装统一下单方法	
  private CloseableHttpResponse wxPayExecute(Integer packageId,String openId,HttpPost httpPost) throws IOException {

        // 获取套餐金额 还有相关信息
        ChatPackage chatPackage = chatPackageMapper.selectById(packageId);

        if (null == chatPackage) {
            throw new NingException(ServiceCode.FAILED);
        }

        BigDecimal amount = chatPackage.getAmount();

        if (null == amount || amount.equals(BigDecimal.ZERO)) {
            throw new NingException(ServiceCode.SUCCESS);
        }
		
		// 从登录信息中获取用户信息
        TokenUser loginUserInfo = CommUtils.getLoginUserInfo();
        Integer userId = loginUserInfo.getUserId();

        // 请求body参数
        Gson gson = new Gson();
        Map<String,Object> paramsMap = new HashMap<>();
        paramsMap.put("appid", wxPayConfig.getAppid());
        paramsMap.put("mchid", wxPayConfig.getMchId());
        paramsMap.put("description", chatPackage.getName());
        paramsMap.put("out_trade_no", WxPayUtil.generateOrderNumber(userId,packageId)); //订单号
        paramsMap.put("notify_url", wxPayConfig.getNotifyDomain().concat(WxApiType.NATIVE_NOTIFY.getType()));

        Map<String,Object> amountMap = new HashMap<>();
        //由单位:元 转换为单位:分,并由Bigdecimal转换为整型
        BigDecimal total = amount.multiply(new BigDecimal(100));

        amountMap.put("total", total.intValue());
        amountMap.put("currency", "CNY");

        paramsMap.put("amount", amountMap);

		// 判断是Navicat下单还是JSAPI下单 JSAPI需要传OPENID
        if (StringUtils.isNotBlank(openId)) {
            Map<String,Object> payerMap = new HashMap<>();
            payerMap.put("openid",openId);
            paramsMap.put("payer",payerMap);
        }


        JSONObject attachJson = new JSONObject();
        attachJson.put("packageId",packageId);
        attachJson.put("userId",userId);
        attachJson.put("total",total);

        paramsMap.put("attach",attachJson.toJSONString());


        //将参数转换成json字符串
        String jsonParams = gson.toJson(paramsMap);
        log.info("请求参数 ===> {}" , jsonParams);

        StringEntity entity = new StringEntity(jsonParams, "utf-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");

        //完成签名并执行请求
        return wxPayClient.execute(httpPost);
    }

六、微信支付回调接口

1.Controller层

   /**
     * 支付通知
     * 微信支付通过支付通知接口将用户支付成功消息通知给商户
     */
    @ApiOperation(value = "支付通知", notes = "支付通知")
    @PostMapping("/pay/notify")
    @ClientAuthControl
    public WxRes nativeNotify() {
        return wxPayService.nativeNotify();
    }
    

2.Service层

    WxRes nativeNotify();

3.实现层


	@Resource
    private Verifier verifier;
    
    private final ReentrantLock lock = new ReentrantLock();

    @Override
    @SneakyThrows
    @Transactional
    public WxRes nativeNotify() {
        HttpServletRequest request = CommUtils.getRequest();
        HttpServletResponse response = CommUtils.getResponse();

        Gson gson = new Gson();

        try {
            //处理通知参数
            String body = WxPayUtil.readData(request);
            Map<String, Object> bodyMap = gson.fromJson(body, HashMap.class);
            String requestId = (String) bodyMap.get("id");

            //签名的验证
            WechatPay2ValidatorForRequest wechatPay2ValidatorForRequest
                    = new WechatPay2ValidatorForRequest(verifier, requestId, body);
            if (wechatPay2ValidatorForRequest.validate(request)) {
                throw new RuntimeException();
            }

            log.info("通知验签成功");

            //处理订单
            processOrder(bodyMap);

            return new WxRes("SUCCESS","成功");
        } catch (Exception e) {
            e.printStackTrace();

            response.setStatus(500);

            return new WxRes("FAIL","成功");
        }
    }

   /**
     * 处理订单
     *
     * @param bodyMap
     */
    @Transactional
    @SneakyThrows
    public void processOrder(Map<String, Object> bodyMap){
        log.info("处理订单");

        //解密报文
        String plainText = decryptFromResource(bodyMap);

        //将明文转换成map
        Gson gson = new Gson();
        HashMap plainTextMap = gson.fromJson(plainText, HashMap.class);
        String orderNo = (String) plainTextMap.get("out_trade_no");
        String attach = (String) plainTextMap.get("attach");
        JSONObject attachJson = JSONObject.parseObject(attach);
        Integer packageId = attachJson.getInteger("packageId");
        Integer userId = attachJson.getInteger("userId");
        Integer total = attachJson.getInteger("total");


        /*在对业务数据进行状态检查和处理之前,
        要采用数据锁进行并发控制,
        以避免函数重入造成的数据混乱*/
        //尝试获取锁:
        // 成功获取则立即返回true,获取失败则立即返回false。不必一直等待锁的释放
        if (lock.tryLock()) {
            try {

                log.info("plainText={}",plainText);

                //处理重复的通知
                //接口调用的幂等性:无论接口被调用多少次,产生的结果是一致的。
                String orderStatus = orderService.getOrderStatus(orderNo);
                if (!OrderStatus.NOTPAY.getType().equals(orderStatus)) {
                    return;
                }

				// TODO 修改订单状态、添加支付记录等
              


                // 通知前端用户 已完成支付
                messageSocketHandle.sendMessageByUserID(userId,new TextMessage("PaySuccess"));

            } finally {
                //要主动释放锁
                lock.unlock();
            }
        }
    }

	/**
     * 对称解密
     *
     * @param bodyMap
     * @return
     */

    @SneakyThrows
    private String decryptFromResource(Map<String, Object> bodyMap) {

        log.info("密文解密");

        //通知数据
        Map<String, String> resourceMap = (Map) bodyMap.get("resource");
        //数据密文
        String ciphertext = resourceMap.get("ciphertext");
        //随机串
        String nonce = resourceMap.get("nonce");
        //附加数据
        String associatedData = resourceMap.get("associated_data");

        AesUtil aesUtil = new AesUtil(wxPayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8));
        //数据明文
        String plainText = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),
                nonce.getBytes(StandardCharsets.UTF_8),
                ciphertext);

        return plainText;
    }

七、工具类和相关配置类

1.WxPayUtil工具类

@Slf4j
public class WxPayUtil {

    private static final Random random = new Random();

	// 生成订单号
    public static String generateOrderNumber(int userId, int packageId) {
        // 获取当前时间戳
        long timestamp = System.currentTimeMillis();

        // 生成6位随机数
        int randomNum = random.nextInt(900000) + 100000;

        // 组装订单号
        return String.format("%d%d%d%d", timestamp, randomNum, userId, packageId);
    }


    /**
     * 生成二维码
     * @param url
     */
    public static void makeQRCode(String url){

        HttpServletResponse response = CommUtils.getResponse();

        //通过支付链接生成二维码
        HashMap<EncodeHintType, Object> hints = new HashMap<>();
        hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M);
        hints.put(EncodeHintType.MARGIN, 2);
        try {
            BitMatrix bitMatrix = new MultiFormatWriter().encode(url, BarcodeFormat.QR_CODE, 200, 200, hints);
            MatrixToImageWriter.writeToStream(bitMatrix, "PNG", response.getOutputStream());
            System.out.println("创建二维码完成");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 将通知参数转化为字符串
     *
     * @param request
     * @return
     */
    public static String readData(HttpServletRequest request) {
        BufferedReader br = null;
        try {
            StringBuilder result = new StringBuilder();
            br = request.getReader();
            for (String line; (line = br.readLine()) != null; ) {
                if (result.length() > 0) {
                    result.append("\n");
                }
                result.append(line);
            }
            return result.toString();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

2.微信支付配置类

@Configuration
@ConfigurationProperties(prefix = "wxpay") //读取wxpay节点
@Data //使用set方法将wxpay节点中的值填充到当前类的属性中
@Slf4j
public class WxPayConfig {

    // 商户号
    private String mchId;

    // 商户API证书序列号
    private String mchSerialNo;

    // 商户私钥文件
    private String privateKeyPath;

    // APIv3密钥
    private String apiV3Key;

    // APPID
    private String appid;

    // 微信服务器地址
    private String domain;

    // 接收结果通知地址
    private String notifyDomain;

    /**
     * 获取商户的私钥文件
     *
     * @param filename
     * @return
     */
    private PrivateKey getPrivateKey(String filename) {

        try {
            return PemUtil.loadPrivateKey(new FileInputStream(filename));
        } catch (FileNotFoundException e) {
            throw new RuntimeException("私钥文件不存在", e);
        }
    }

    /**
     * 获取签名验证器
     *
     * @return
     */
    @Bean
    public ScheduledUpdateCertificatesVerifier getVerifier() {

        log.info("获取签名验证器");

        //获取商户私钥
        PrivateKey privateKey = getPrivateKey(privateKeyPath);

        //私钥签名对象
        PrivateKeySigner privateKeySigner = new PrivateKeySigner(mchSerialNo, privateKey);

        //身份认证对象
        WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mchId, privateKeySigner);

        // 使用定时更新的签名验证器,不需要传入证书
        ScheduledUpdateCertificatesVerifier verifier = new ScheduledUpdateCertificatesVerifier(
                wechatPay2Credentials,
                apiV3Key.getBytes(StandardCharsets.UTF_8));

        return verifier;
    }


    /**
     * 获取http请求对象
     *
     * @param verifier
     * @return
     */
    @Bean(name = "wxPayClient")
    public CloseableHttpClient getWxPayClient(ScheduledUpdateCertificatesVerifier verifier) {

        log.info("获取httpClient");

        //获取商户私钥
        PrivateKey privateKey = getPrivateKey(privateKeyPath);

        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(mchId, mchSerialNo, privateKey)
                .withValidator(new WechatPay2Validator(verifier));
        // ... 接下来,你仍然可以通过builder设置各种参数,来配置你的HttpClient

        // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
        CloseableHttpClient httpClient = builder.build();

        return httpClient;
    }

    /**
     * 获取HttpClient,无需进行应答签名验证,跳过验签的流程
     */
    @Bean(name = "wxPayNoSignClient")
    public CloseableHttpClient getWxPayNoSignClient() {

        //获取商户私钥
        PrivateKey privateKey = getPrivateKey(privateKeyPath);

        //用于构造HttpClient
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                //设置商户信息
                .withMerchant(mchId, mchSerialNo, privateKey)
                //无需进行签名验证、通过withValidator((response) -> true)实现
                .withValidator((response) -> true);

        // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
        CloseableHttpClient httpClient = builder.build();

        log.info("== getWxPayNoSignClient END ==");

        return httpClient;
    }

}

3.微信支付枚举类

@AllArgsConstructor
@Getter
public enum WxApiType {

    /**
     * Native下单
     */
    NATIVE_PAY("/v3/pay/transactions/native"),

    /**
     * JSAPI下单
     */
    JSAPI_PAY("/v3/pay/transactions/jsapi"),

    /**
     * 查询订单
     */
    ORDER_QUERY_BY_NO("/v3/pay/transactions/out-trade-no/%s"),

    /**
     * 关闭订单
     */
    CLOSE_ORDER_BY_NO("/v3/pay/transactions/out-trade-no/%s/close"),

    /**
     * 支付通知
     */
    NATIVE_NOTIFY("/client/order/pay/notify");

    /**
     * 类型
     */
    private final String type;
}

4.签名验证类

@Slf4j
public class WechatPay2ValidatorForRequest {
    /**
     * 应答超时时间,单位为分钟
     */
    protected static final long RESPONSE_EXPIRED_MINUTES = 5;
    protected final Verifier verifier;
    protected final String requestId;
    protected final String body;


    public WechatPay2ValidatorForRequest(Verifier verifier, String requestId, String body) {
        this.verifier = verifier;
        this.requestId = requestId;
        this.body = body;
    }

    protected static IllegalArgumentException parameterError(String message, Object... args) {
        message = String.format(message, args);
        return new IllegalArgumentException("parameter error: " + message);
    }

    protected static IllegalArgumentException verifyFail(String message, Object... args) {
        message = String.format(message, args);
        return new IllegalArgumentException("signature verify fail: " + message);
    }

    public final boolean validate(HttpServletRequest request) throws IOException {
        try {
            //处理请求参数
            validateParameters(request);

            //构造验签名串
            String message = buildMessage(request);

            String serial = request.getHeader(WECHAT_PAY_SERIAL);
            String signature = request.getHeader(WECHAT_PAY_SIGNATURE);

            //验签
            if (!verifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature)) {
                throw verifyFail("serial=[%s] message=[%s] sign=[%s], request-id=[%s]",
                        serial, message, signature, requestId);
            }
        } catch (IllegalArgumentException e) {
            log.error(e.getMessage());
            return false;
        }

        return true;
    }

    protected final void validateParameters(HttpServletRequest request) {

        // NOTE: ensure HEADER_WECHAT_PAY_TIMESTAMP at last
        String[] headers = {WECHAT_PAY_SERIAL, WECHAT_PAY_SIGNATURE, WECHAT_PAY_NONCE, WECHAT_PAY_TIMESTAMP};

        String header = null;
        for (String headerName : headers) {
            header = request.getHeader(headerName);
            if (header == null) {
                throw parameterError("empty [%s], request-id=[%s]", headerName, requestId);
            }
        }

        //判断请求是否过期
        String timestampStr = header;
        try {
            Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestampStr));
            // 拒绝过期请求
            if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= RESPONSE_EXPIRED_MINUTES) {
                throw parameterError("timestamp=[%s] expires, request-id=[%s]", timestampStr, requestId);
            }
        } catch (DateTimeException | NumberFormatException e) {
            throw parameterError("invalid timestamp=[%s], request-id=[%s]", timestampStr, requestId);
        }
    }

    protected final String buildMessage(HttpServletRequest request) throws IOException {
        String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
        String nonce = request.getHeader(WECHAT_PAY_NONCE);
        return timestamp + "\n"
                + nonce + "\n"
                + body + "\n";
    }

    protected final String getResponseBody(CloseableHttpResponse response) throws IOException {
        HttpEntity entity = response.getEntity();
        return (entity != null && entity.isRepeatable()) ? EntityUtils.toString(entity) : "";
    }

}

OK,齐活~

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 实现微信支付详细教程 的相关文章

  • Java:如何将ArrayList作为对象的实例变量?

    我正在开展一个班级项目 用 Java 构建一个小型 Connect4 游戏 我目前的想法是拥有一类 Columns 它具有一些整数 索引 最大长度 isFull 作为实例变量 以及一个 ArrayList 来接收上面的整数和每个玩家的表现
  • 无法使用 Maven 编译简单的 Java 10 / Java 11 项目

    我有一个简单的 Maven 项目 src main java module info java pom xml pom xml
  • 正则表达式奇/偶数

    我有一个正则表达式问题 我不知道该怎么做 它必须匹配开头包含任意数量的 a 的所有字符串 如果 a 的数量为偶数 则匹配单个 0 如果 a 的数量为奇数 则匹配单个 1 如何跟踪偶数 奇数 Sample aaa1 aaaa0 a aa 1
  • Maven:在项目之间共享源

    我有两个项目 它们是一个共同父项目的子项目 还有一个生成器项目和生成器对应的maven插件项目 此外 两个子项目都使用相同的输入文件 该文件用于代码生成 parent pom child1 jar src main generator in
  • Spring Cloud Gateway + Spring安全资源服务器

    我真的不会把它放在这里 但我真的很困惑 我想实现以下目标 我在跑步 Java 14 Spring Cloud Gateway版本 Hoxton SR3 Spring Boot版本 2 2 5 RELEASE 现在我想将安全性集成到我的网关和
  • 何时使用环境变量与系统属性?

    我想知道以下哪种方法是首选方法 我们可以将事情设置为APP HOME path to file export in profile或类似的东西 并将其访问为System getenv APP HOME 或者 也可以使用属性作为 DAPP H
  • Checkstyle 规则防止调用某些方法和构造函数

    是否可以使用 Checkstyle 来禁止使用某些使用系统相关默认值 区域设置 字符集等 的构造函数或方法 我更喜欢强制执行一项政策 程序员应该明确了解系统相关的值 所以我认为以下物品是危险的 all the constructors of
  • Java俄罗斯方块旋转

    我知道这个问题已经被问了很多 但我想知道如何旋转俄罗斯方块 我已经做了一个又长又糟糕的解决方案 大约 170 行代码 但应该有更简单的方法来做到这一点 我的俄罗斯方块由 4 个块组成 它们都知道它们在矩阵中的位置 行和列 Matrix本身是
  • 如何配置jackson属性命名策略?

    此代码不起作用 Configuration public class RepositoryRestMvcConfig extends RepositoryRestMvcConfiguration Bean Override public O
  • 在 JSON 转换为 CSV 期间保持 JSON 键的顺序

    我正在使用此处提供的 JSON 库http www json org java index html http www json org java index html为了将 json 字符串转换为 CSV 但我遇到的问题是 转换后键的顺序
  • 如果Jetty的密钥库中有多个证书,它如何选择?

    我们的系统中有一些代码用于自动将自签名证书生成到密钥库中 然后由 Jetty 使用 如果给定主机的密钥已经存在 那么什么也不会发生 但如果它不存在 我们会生成一个新密钥 如下所示 public void generateKey String
  • 在其抽象超类中使用子类的泛型类型?

    在我的代码中有以下抽象超类 public abstract class AbstractClass
  • 带有 Spock Stub 的泛型

    我无法为泛型类编译 Spock 存根 构造函数的签名如下 SomeClass SerSup
  • cipher.update在java中做什么?

    我正在实施 DES CBC 我很困惑什么cipher init cipher update and cipher dofinal做 我只是使用 init 来设置密钥dofinal得到结果 我不使用更新 那是对的吗 另外使用时结果有什么不同U
  • 如何使用 Java glob 模式搜索(区分大小写)文件?

    我正在检查获取路径匹配器 http docs oracle com javase 7 docs api java nio file FileSystem html getPathMatcher 28java lang String 29的方
  • JVM 是否会内联对象的实例变量和方法?

    假设我有一个非常紧密的内部循环 每次迭代都会访问和改变一个簿记对象 该对象存储有关算法的一些简单数据 并具有用于操作它的简单逻辑 簿记对象是私有的和最终的 并且它的所有方法都是私有的 最终的和 inline 下面是一个示例 Scala 语法
  • 策略模式的现实示例

    我一直在读关于OCP原理 http en wikipedia org wiki Open closed principle以及如何使用策略模式来实现这一目标 我打算尝试向几个人解释这一点 但我能想到的唯一例子是根据 订单 的状态使用不同的验
  • Spring JPA (Hibernate) Entity Manager 何时将连接返回到连接池?

    在我的 java 进程中 我使用以下 spring 配置连接到 MySql Configuration EnableTransactionManagement PropertySources PropertySource classpath
  • struts2中如何访问url参数

    我正在做一个struts2项目 我在项目中创建了 url 并使用标签传递了参数 我的问题是如何读取操作中的参数 另外 如果执行相同的操作 我将能够将参数视为查询字符串 我问是因为我无法做到 而且我在其中一个教程中看到了它 通常 您将通过使用
  • 使用 jar 外部的配置启动 .jar,但能够从任何当前目录执行此操作

    我一直在努力解决一个特定的问题 我在 jar 文件中有一个 Java 程序 并且在 jar 之外有一个 lib 目录和一个 config 目录 但与 jar 本身所在的目录位于同一目录中 我正在尝试参考config foo config从代

随机推荐

  • 【TensorFlow】tf.nn.conv2d卷积实现+filter计算原理

    本来想转载底层实现过程的 结果发现原文中的实验2错误 在这里说明下fliter工作过程 输入层 就是输入的图像 有可能是三通道的有可能是单通道的 filter 深度与输入层的深度 通道数 相同 输出层 输出层的就是filter遍历输入层后的
  • mysql 创建索引 强制_mysql 强制走索引

    查询是数据库技术中最常用的操作 查询操作的过程比较简单 首先从客户端发出查询的SQL语句 数据库服务端在接收到由客户端发来的SQL语句后 执行这条SQL语句 然后将查询到的结果返回给客户端 虽然过程很简单 但不同的查询方式和数据库设置 对查
  • 汇编程序设计与计算机体系结构软件工程师教程笔记:函数、字符串、浮点运算

    汇编程序设计与计算机体系结构 软件工程师教程 这本书是由Brain R Hall和Kevin J Slonka著 由爱飞翔译 中文版是2019年出版的 个人感觉这本书真不错 书中介绍了三种汇编器GAS NASM MASM异同 全部示例代码都
  • 得物 H5容器 野指针疑难问题排查 & 解决

    1背景 得物 iOS 4 9 x 版本 上线后 一些带有横向滚动内容的h5页面 有一个webkit 相关crash增加较快 通过Crash堆栈判断是UIScrollview执行滚动动画过程中内存野指针导致的崩溃 2前期排查 通过页面浏览日志
  • Maven 导出依赖Jar,生成source.jar,javadoc.jar

    下载最新版的Maven http maven apache org download cgi 解压到本地文件夹 新建环境变量 MAVEN HOME maven解压目录 在path加入 MAVEN HOME bin 需要确保已经有Java环境
  • matlab修改坐标轴刻度间隔大小

    修改matlab画图的坐标轴的间隔大小 使用下边的 set gca XTick 2 2 46 改变x轴坐标间隔显示 这里间隔为2
  • kettle mysql jdbc_kettle作业连接mysql资源库8小时后报错

    kettle作业在运行一段时间后会报错 原因是mysql会默认每8小时回收一次无用连接 错误日志如下 Communications link failure with MySQL he last packet successfully re
  • ubuntu安装android sdk

    1 下载Android SDK http tools android studio org index php sdk 我下载的是 android sdk r24 4 1 linux tgz 解压Android SDK 将android s
  • 忘掉 MindNode, 这才是Mac颜值最高的思维导图工具

    思维导图是一款帮助我们将大脑中浮现的各种思维串联起来的工具 非常适合用于逻辑性写作或者团队的头脑风暴 应用的范围比较广 关于写如何用思维导图工具的文章多如牛毛 不管是对职场人士 学生 还是全职妈妈的作用 各种文章都有涉猎 既然思维导图工具如
  • 安装CentOS7.6并创建用户及优化系统

    文章目录 安装系统 创建用户及基础优化系统 安装系统 制作CentOS7 6的镜像光盘或U盘 略过 将光盘或U盘放入到服务器中 修改BIOS启动选项 将其修改为光盘或U盘启动 启动服务器 如果是物理机的话 启动服务器后 进入远控卡 设置远控
  • JAVA socket编程实例

    转载文章 原作者无从考证 感谢作者的无私奉献 事实上网络编程简单的理解就是两台计算机相互通讯数据而已 对于程序员而言 去掌握一种编程接口并使用一种编程模型相对就会显得简单的多了 Java SDK提供一些相对简单的Api来完成这些工作 Soc
  • 【实战练习】汽油辛烷值优化建模(一)(题目+数据集)

    先放上题目和数据集 链接 https pan baidu com s 15 iDC9Wdx49rUe Qt2b Uw 提取码 6666 一 题目 1 背景 汽油是小型车辆的主要燃料 汽油燃烧产生的尾气排放对大气环境有重要影响 为此 世界各国
  • 《Qt快速入门》-- 信号与槽机制

    每一个图形开发语言 工具都有自己的一套的ui交互机制 Qt也不例外 Qt有自己独特的信号与槽机制用于ui与功能算法的交互 Qt的信号与槽机制包含以下三点 1 确定是哪个控件发出了信号 Who 2 确定发出了什么信号 What 3 确定这个信
  • java必懂之"=="与equals的区别

    屁话不多说 直接上代码 equals和关系运算符 的区别 author 刘威辰的秘密花园 1 用在基本数据类型boolean a b 2 判断引用是否指向同一个地址且内容是否相同 equals 1 用于判断两个变量是否对同一个对象的引用 即
  • Django报错403 Forbidden. CSRF token missing or incorrect的解决办法

    Django报错403 Forbidden CSRF token missing or incorrect的解决办法 首先要确认自己在views py中使用的是render 之后确认自己在xx html中的
  • KCF高速跟踪详解

    思想 一般化的跟踪问题可以分解成如下几步 1 在 It 帧中 在当前位置 pt 附近采样 训练一个回归器 这个回归器能计算一个小窗口采样的响应 2 在 It 1 帧中 在前一帧位置 pt 附近采样 用前述回归器判断每个采样的响应 3 响应最
  • IntelliJ IDEA 如何创建一个包,并在包中创建一个Java程序

    1 选中scr右键后 将鼠标放到New上 点击Package 2 采用域名倒置的方式对包名进行命名 3 选中包后 鼠标右键选中New 点击Java Class 完成一个Java程序的创建
  • Python算法:深度优先搜索—DFS(模板及其样例)

    深度优先搜索搜索 介绍 沿着一条路径一直搜索下去 在无法搜索时 回退到刚刚访问过的节点 并且每个节点只能访问一次 本质上是持续搜索 遍历了所有可能的情况 必然能得到解 流程是一个树的形式 每次一条路走到黑 目的主要是达到被搜索结构的叶结点直
  • Nginx学习与实战 · 解决net::ERR_CONTENT_LENGTH_MISMATCH 206问题

    Vue项目引入了d3 js 在打包部署到nginx静态服务后 页面不能正常展示 F12打开控制台 发现报了几个net ERR CONTENT LENGTH MISMATCH 206 Partial Content 错误 第一次遇到Statu
  • Java 实现微信支付详细教程

    摘要 最近的一个项目中涉及到了支付业务 其中用到了微信支付和支付宝支付 在做的过程中也遇到些问题 所以现在总结梳理一下 分享给有需要的人 也为自己以后回顾留个思路 一 微信支付接入准备工作 首先 微信支付 只支持企业用户 个人用户是不能接入