0201基础集成与使用-微信支付-支付模块-项目实战

文章目录

    • 一、前言
    • 二、springboot集成
      • 2.1 配置信息与配置类
      • 2.2 微信相关枚举信息
      • 2.3 工具类
      • 2.4 业务接口
    • 三、演示-支付与退款
    • 结语

一、前言

下面我以微信支付v3为例,通过spirngboot集成到我们的项目中,不依赖其他第三方框架。当然适用简单项目,后续需要更多的改进。项目为简单的一个在线课程项目,通过在线购买视频课程,生成和支付订单,完成在线内容的学习。

二、springboot集成

2.1 配置信息与配置类

  • 配置文件
# 微信支付相关参数
# 商户号
wxpay.mch-id=xxxx
# 商户API证书序列号
wxpay.mch-serial-no=xxxx# 商户私钥文件
wxpay.private-key-path=xxxx
# APIv3密钥
wxpay.api-v3-key=XXXX
# APPID
wxpay.appid=
# 微信服务器地址
wxpay.domain=https://api.mch.weixin.qq.com
# 接收结果通知地址
# 注意:每次重新启动ngrok,都需要根据实际情况修改这个配置
wxpay.notify-domain=XXXX# APIv2密钥
wxpay.partnerKey: XXX
  • 相应的参数值,根据官方文档自己去申请,如果只是学习研究,可以通过下面链接1获取。

  • wxpay.notify-domain:微信回调通知地址,如果有在公网部署用公网地址,没有的话通过内网地址穿透工具,这里推荐开源免费的ngrok工具。

  • 配置类

    package com.gaogzhen.paymentdemo.config;import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
    import com.wechat.pay.contrib.apache.httpclient.auth.*;
    import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
    import lombok.Data;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.PropertySource;import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.nio.charset.StandardCharsets;
    import java.security.PrivateKey;@Configuration
    @PropertySource("classpath:wxpay.properties") //读取配置文件
    @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;// APPIDprivate String appid;// 微信服务器地址private String domain;// 接收结果通知地址private String notifyDomain;// APIv2密钥private String partnerKey;/*** 获取商户的私钥文件* @param filename* @return*/private PrivateKey getPrivateKey(String filename){try {return PemUtil.loadPrivateKey(new FileInputStream(filename));} catch (FileNotFoundException e) {throw new RuntimeException("私钥文件不存在", e);}}/*** 获取签名验证器* @return*/@Beanpublic 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);//用于构造HttpClientWechatPayHttpClientBuilder 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;}}
    • 使用定时更新的签名验证器,不需要传入证书:这里我们需要验证传回来的信息是微信返回的那么使用的是微信平台证书;相应的微信那边需要验证它接受的信息是我们商户这边发送过去的,需要用到商户平台证书。

2.2 微信相关枚举信息

  • 微信API接口枚举WxApiType

    package com.gaogzhen.paymentdemo.enums.wxpay;import lombok.AllArgsConstructor;
    import lombok.Getter;@AllArgsConstructor
    @Getter
    public enum WxApiType {/*** Native下单*/NATIVE_PAY("/v3/pay/transactions/native"),/*** Native下单*/NATIVE_PAY_V2("/pay/unifiedorder"),/*** 查询订单*/ORDER_QUERY_BY_NO("/v3/pay/transactions/out-trade-no/%s"),/*** 关闭订单*/CLOSE_ORDER_BY_NO("/v3/pay/transactions/out-trade-no/%s/close"),/*** 申请退款*/DOMESTIC_REFUNDS("/v3/refund/domestic/refunds"),/*** 查询单笔退款*/DOMESTIC_REFUNDS_QUERY("/v3/refund/domestic/refunds/%s"),/*** 申请交易账单*/TRADE_BILLS("/v3/bill/tradebill"),/*** 申请资金账单*/FUND_FLOW_BILLS("/v3/bill/fundflowbill");/*** 类型*/private final String type;
    }
  • 支付状态

    package com.gaogzhen.paymentdemo.enums.wxpay;import lombok.AllArgsConstructor;
    import lombok.Getter;@AllArgsConstructor
    @Getter
    public enum WxApiType {/*** Native下单*/NATIVE_PAY("/v3/pay/transactions/native"),/*** Native下单*/NATIVE_PAY_V2("/pay/unifiedorder"),/*** 查询订单*/ORDER_QUERY_BY_NO("/v3/pay/transactions/out-trade-no/%s"),/*** 关闭订单*/CLOSE_ORDER_BY_NO("/v3/pay/transactions/out-trade-no/%s/close"),/*** 申请退款*/DOMESTIC_REFUNDS("/v3/refund/domestic/refunds"),/*** 查询单笔退款*/DOMESTIC_REFUNDS_QUERY("/v3/refund/domestic/refunds/%s"),/*** 申请交易账单*/TRADE_BILLS("/v3/bill/tradebill"),/*** 申请资金账单*/FUND_FLOW_BILLS("/v3/bill/fundflowbill");/*** 类型*/private final String type;
    }
  • 退款状态

    package com.gaogzhen.paymentdemo.enums.wxpay;import lombok.AllArgsConstructor;
    import lombok.Getter;@AllArgsConstructor
    @Getter
    public enum WxRefundStatus {/*** 退款成功*/SUCCESS("SUCCESS"),/*** 退款关闭*/CLOSED("CLOSED"),/*** 退款处理中*/PROCESSING("PROCESSING"),/*** 退款异常*/ABNORMAL("ABNORMAL");/*** 类型*/private final String type;
    }

2.3 工具类

  • 接口协议为http,通过HttpClientUtils工具类执行接口请求,或者可以使用其他的http工具类比如基础封装HttpClient,OkHttp或者hutool封装的http工具类,或者Forest框架等等是,这里我们就简单封装下,代码如下:

    package com.gaogzhen.paymentdemo.util;import org.apache.http.Consts;
    import org.apache.http.HttpEntity;
    import org.apache.http.NameValuePair;
    import org.apache.http.client.ClientProtocolException;
    import org.apache.http.client.entity.UrlEncodedFormEntity;
    import org.apache.http.client.methods.*;
    import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
    import org.apache.http.conn.ssl.SSLContextBuilder;
    import org.apache.http.conn.ssl.TrustStrategy;
    import org.apache.http.entity.StringEntity;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.message.BasicNameValuePair;
    import org.apache.http.util.EntityUtils;import javax.net.ssl.SSLContext;
    import java.io.IOException;
    import java.security.cert.CertificateException;
    import java.security.cert.X509Certificate;
    import java.text.ParseException;
    import java.util.HashMap;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Map;/*** http请求客户端*/
    public class HttpClientUtils {private String url;private Map<String, String> param;private int statusCode;private String content;private String xmlParam;private boolean isHttps;public boolean isHttps() {return isHttps;}public void setHttps(boolean isHttps) {this.isHttps = isHttps;}public String getXmlParam() {return xmlParam;}public void setXmlParam(String xmlParam) {this.xmlParam = xmlParam;}public HttpClientUtils(String url, Map<String, String> param) {this.url = url;this.param = param;}public HttpClientUtils(String url) {this.url = url;}public void setParameter(Map<String, String> map) {param = map;}public void addParameter(String key, String value) {if (param == null)param = new HashMap<String, String>();param.put(key, value);}public void post() throws ClientProtocolException, IOException {HttpPost http = new HttpPost(url);setEntity(http);execute(http);}public void put() throws ClientProtocolException, IOException {HttpPut http = new HttpPut(url);setEntity(http);execute(http);}public void get() throws ClientProtocolException, IOException {if (param != null) {StringBuilder url = new StringBuilder(this.url);boolean isFirst = true;for (String key : param.keySet()) {if (isFirst) {url.append("?");isFirst = false;}else {url.append("&");}url.append(key).append("=").append(param.get(key));}this.url = url.toString();}HttpGet http = new HttpGet(url);execute(http);}/*** set http post,put param*/private void setEntity(HttpEntityEnclosingRequestBase http) {if (param != null) {List<NameValuePair> nvps = new LinkedList<NameValuePair>();for (String key : param.keySet())nvps.add(new BasicNameValuePair(key, param.get(key))); // 参数http.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8)); // 设置参数}if (xmlParam != null) {http.setEntity(new StringEntity(xmlParam, Consts.UTF_8));}}private void execute(HttpUriRequest http) throws ClientProtocolException,IOException {CloseableHttpClient httpClient = null;try {if (isHttps) {SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {// 信任所有public boolean isTrusted(X509Certificate[] chain,String authType)throws CertificateException {return true;}}).build();SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();} else {httpClient = HttpClients.createDefault();}CloseableHttpResponse response = httpClient.execute(http);try {if (response != null) {if (response.getStatusLine() != null)statusCode = response.getStatusLine().getStatusCode();HttpEntity entity = response.getEntity();// 响应内容content = EntityUtils.toString(entity, Consts.UTF_8);}} finally {response.close();}} catch (Exception e) {e.printStackTrace();} finally {httpClient.close();}}public int getStatusCode() {return statusCode;}public String getContent() throws ParseException, IOException {return content;}}

2.4 业务接口

也就是我们controller中对外暴露的接口,代码如下

package com.gaogzhen.paymentdemo.controller;import com.gaogzhen.paymentdemo.service.WxPayService;
import com.gaogzhen.paymentdemo.util.HttpUtils;
import com.gaogzhen.paymentdemo.util.WechatPay2ValidatorForRequest;
import com.gaogzhen.paymentdemo.vo.R;
import com.google.gson.Gson;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;@CrossOrigin //跨域
@RestController
@RequestMapping("/api/wx-pay")
@Api(tags = "网站微信支付APIv3")
@Slf4j
public class WxPayController {@Resourceprivate WxPayService wxPayService;@Resourceprivate Verifier verifier;/*** Native下单* @param productId* @return* @throws Exception*/@ApiOperation("调用统一下单API,生成支付二维码")@PostMapping("/native/{productId}")public R nativePay(@PathVariable Long productId) throws Exception {log.info("发起支付请求 v3");//返回支付二维码连接和订单号Map<String, Object> map = wxPayService.nativePay(productId);return R.ok().setData(map);}/*** 支付通知* 微信支付通过支付通知接口将用户支付成功消息通知给商户*/@ApiOperation("支付通知")@PostMapping("/native/notify")public String nativeNotify(HttpServletRequest request, HttpServletResponse response){Gson gson = new Gson();Map<String, String> map = new HashMap<>();//应答对象try {//处理通知参数String body = HttpUtils.readData(request);Map<String, Object> bodyMap = gson.fromJson(body, HashMap.class);String requestId = (String)bodyMap.get("id");log.info("支付通知的id ===> {}", requestId);//log.info("支付通知的完整数据 ===> {}", body);//int a = 9 / 0;//签名的验证WechatPay2ValidatorForRequest wechatPay2ValidatorForRequest= new WechatPay2ValidatorForRequest(verifier, requestId, body);if(!wechatPay2ValidatorForRequest.validate(request)){log.error("通知验签失败");//失败应答response.setStatus(500);map.put("code", "ERROR");map.put("message", "通知验签失败");return gson.toJson(map);}log.info("通知验签成功");//处理订单wxPayService.processOrder(bodyMap);//应答超时//模拟接收微信端的重复通知TimeUnit.SECONDS.sleep(5);//成功应答response.setStatus(200);map.put("code", "SUCCESS");map.put("message", "成功");return gson.toJson(map);} catch (Exception e) {e.printStackTrace();//失败应答response.setStatus(500);map.put("code", "ERROR");map.put("message", "失败");return gson.toJson(map);}}/*** 用户取消订单* @param orderNo* @return* @throws Exception*/@ApiOperation("用户取消订单")@PostMapping("/cancel/{orderNo}")public R cancel(@PathVariable String orderNo) throws Exception {log.info("取消订单");wxPayService.cancelOrder(orderNo);return R.ok().setMessage("订单已取消");}/*** 查询订单* @param orderNo* @return* @throws Exception*/@ApiOperation("查询订单:测试订单状态用")@GetMapping("/query/{orderNo}")public R queryOrder(@PathVariable String orderNo) throws Exception {log.info("查询订单");String result = wxPayService.queryOrder(orderNo);return R.ok().setMessage("查询成功").data("result", result);}@ApiOperation("申请退款")@PostMapping("/refunds/{orderNo}/{reason}")public R refunds(@PathVariable String orderNo, @PathVariable String reason) throws Exception {log.info("申请退款");wxPayService.refund(orderNo, reason);return R.ok();}/*** 查询退款* @param refundNo* @return* @throws Exception*/@ApiOperation("查询退款:测试用")@GetMapping("/query-refund/{refundNo}")public R queryRefund(@PathVariable String refundNo) throws Exception {log.info("查询退款");String result = wxPayService.queryRefund(refundNo);return R.ok().setMessage("查询成功").data("result", result);}/*** 退款结果通知* 退款状态改变后,微信会把相关退款结果发送给商户。*/@ApiOperation("退款结果通知")@PostMapping("/refunds/notify")public String refundsNotify(HttpServletRequest request, HttpServletResponse response){log.info("退款通知执行");Gson gson = new Gson();Map<String, String> map = new HashMap<>();//应答对象try {//处理通知参数String body = HttpUtils.readData(request);Map<String, Object> bodyMap = gson.fromJson(body, HashMap.class);String requestId = (String)bodyMap.get("id");log.info("支付通知的id ===> {}", requestId);//签名的验证WechatPay2ValidatorForRequest wechatPay2ValidatorForRequest= new WechatPay2ValidatorForRequest(verifier, requestId, body);if(!wechatPay2ValidatorForRequest.validate(request)){log.error("通知验签失败");//失败应答response.setStatus(500);map.put("code", "ERROR");map.put("message", "通知验签失败");return gson.toJson(map);}log.info("通知验签成功");//处理退款单wxPayService.processRefund(bodyMap);//成功应答response.setStatus(200);map.put("code", "SUCCESS");map.put("message", "成功");return gson.toJson(map);} catch (Exception e) {e.printStackTrace();//失败应答response.setStatus(500);map.put("code", "ERROR");map.put("message", "失败");return gson.toJson(map);}}@ApiOperation("获取账单url:测试用")@GetMapping("/querybill/{billDate}/{type}")public R queryTradeBill(@PathVariable String billDate,@PathVariable String type) throws Exception {log.info("获取账单url");String downloadUrl = wxPayService.queryBill(billDate, type);return R.ok().setMessage("获取账单url成功").data("downloadUrl", downloadUrl);}@ApiOperation("下载账单")@GetMapping("/downloadbill/{billDate}/{type}")public R downloadBill(@PathVariable String billDate,@PathVariable String type) throws Exception {log.info("下载账单");String result = wxPayService.downloadBill(billDate, type);return R.ok().data("result", result);}}

tips::

  • 这里我们只做简单集成,如果生成中使用,需要更完善的配置,比如跨域可以单独做更详细的配置,统一的错误处理,分布式并发一致性,性能优化等等。

三、演示-支付与退款

  1. 确认支付,获取二维码在这里插入图片描述

  2. 微信扫码,支付回调在这里插入图片描述

  3. 输入支付密码,完成支付,调用我们自己的后续处理逻辑在这里插入图片描述

退款流程我们不在详述,看下我们的微信支付记录:在这里插入图片描述

微信支付基础使用,演示完毕,完整前后端代码通过链接1 sgg的视频教程获取。后续我们会借助这个项目,通过集成一些开源的支付工具或者框架,比如jeepay,IJPay,roncoo-pay,spring-boot-pay 来集成支付功能。

在这里插入图片描述

结语

欢迎小伙伴一起学习交流,需要啥工具或者有啥问题随时联系我。

❓QQ:806797785

⭐️源代码地址:https://github.com/gaogzhen

[1]微信支付&支付宝支付视频[CP/OL]

[2]微信支付官方文档[CP/OL]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/298123.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux多进程通信(4)——消息队列从入门到实战!

Linux多进程通信总结——进程间通信看这一篇足够啦&#xff01; 1.基本介绍 1&#xff09;消息队列的本质其实是一个内核提供的链表&#xff0c;内核基于这个链表&#xff0c;实现了一个数据结构&#xff0c;向消息队列中写数据&#xff0c;实际上是向这个数据结构中插入一个…

Vue项目登录页实现获取短信验证码的功能

之前我们写过不需要调后端接口就获取验证码的方法,具体看《无需后端接口,用原生js轻松实现验证码》这个文章。现在我们管理后台有个需求,就是登录页面需要获取验证码,用户可以输入验证码后进行登录。效果如下,当我点击获取验证码后能获取短信验证码: 这里在用户点击获取…

win11安装WSL UbuntuTLS

win11安装WSL WSL 简介WSL 1 VS WSL 2先决要求安装方法一键安装通过「控制面板」安装 WSL 基本命令Linux发行版安装Ubuntu初始化相关设置root用户密码网络工具安装安装1panel面板指导 WSl可视化工具问题总结WSL更新命令错误Ubuntu 启动初始化错误未解决问题 WSL 简介 Windows …

Sybase ASE中的char(N)的坑以及与PostgreSQL的对比

1背景 昨天,一朋友向我咨询Sybase ASE中定长字符串类型的行为,说他们的客户反映,同样的char类型的数据,通过jdbc来查,Sybase库不会带空格,而PostgreSQL会带。是不是这样的?他是PostgreSQL的专业大拿,但因为他手头没有现成的Sybase ASE环境,刚好我手上有,便于一试。 …

hive 慢sql 查询

hive 慢sql 查询 查找 hive 执行日志存储路径&#xff08;一般是 hive-audit.log &#xff09; 比如&#xff1a;/var/log/Bigdata/audit/hive/hiveserver/hive-audit.log 解析日志 获取 执行时间 执行 OperationId 执行人 UserNameroot 执行sql 数据分隔符为 \001 并写入 hiv…

vuepress-theme-hope 添加谷歌统计代码

最近做了个网站,从 cloudflare 来看访问量,过去 30 天访问量竟然有 1.32k 给我整懵逼了,我寻思不应该呀,毕竟这个网站内容还在慢慢补充中,也没告诉别人,怎么就这么多访问?搜索了下, cloudflare 还会把爬虫的请求也就算进来,所以数据相对来说就不是很准确 想到了把 Google An…

如何同时安全高效管理多个谷歌账号?

您的业务活动需要多个 Gmail 帐户吗&#xff1f;出海畅游&#xff0c;Gmail账号是少不了的工具之一&#xff0c;可以关联到Twitter、Facebook、Youtube、Chatgpt等等平台&#xff0c;可以说是海外网络的“万能锁”。但是大家都知道&#xff0c;以上这些平台注册多账号如果产生关…

Cisco交换机安全配置

Cisco交换机安全配置 前提 我们以下命令一般都要先进入Config模式 S1> enable S1# conf t S1(config)#端口安全保护 禁用未使用的端口 以关闭fa0/1到fa0/24的端口为例 S1(config)# interface range fa0/1-24 S1(config-if-range)# shutdown缓解MAC地址表攻击 防止CAM…

蓝桥杯刷题第六天(昨天忘记发了)

今天想从不一样的角度来解题&#xff1a;从时间紧张暴力求解到思路阔达直接通过所有案例 暴力方法&#xff1a; 思路第一眼看到这个问题我就想到了第一个思路就是先用两个数组一个存石子数一个存颜色状态&#xff0c;每次遍历一遍看看有没有相邻石子颜色一样且为和最小的。 im…

前端订阅后端推送WebSocket定时任务

0.需求 后端定时向前端看板推送数据&#xff0c;每10秒或者30秒推送一次。 1.前言知识 HTTP协议是一个应用层协议&#xff0c;它的特点是无状态、无连接和单向的。在HTTP协议中&#xff0c;客户端发起请求&#xff0c;服务器则对请求进行响应。这种请求-响应的模式意味着服务器…

Spring-IoC 基于注解

基于xml方法见&#xff1a;http://t.csdnimg.cn/dir8j 注解是代码中的一种特殊标记&#xff0c;可以在编译、类加载和运行时被读取&#xff0c;执行相应的处理&#xff0c;简化 Spring的 XML配置。 格式&#xff1a;注解(属性1"属性值1",...) 可以加在类上…

纯C代码模板

一、快排 void QuickSort(int *a,int left,int right){if(left>right) return;else{int low left,high right;int pivot a[low];while(low<high){while(a[high] > pivot && low < high){high--;}a[low] a[high]; //必须先动a[low]while(a[low] < …

工厂方法模式与抽象工厂模式的深度对比

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 &#x1f680; 转载自&#xff1a;设计模式深度解析&#xff1a;工厂方法模式与抽象工厂模式的深…

intellij idea 使用git撤销(取消)commit

git撤销(取消) 未 push的 commit Git&#xff0c;选择分支后&#xff0c;右键 Undo Commit &#xff0c;会把这个 commit 撤销。 git撤销(取消) 已经 push 的 commit 备份分支内容&#xff1a; 选中分支&#xff0c; 新建 分支&#xff0c;避免后续因为操作不当&#xff0c;导…

Spring重点知识(个人整理笔记)

目录 1. 为什么要使用 spring&#xff1f; 2. 解释一下什么是 Aop&#xff1f; 3. AOP有哪些实现方式&#xff1f; 4. Spring AOP的实现原理 5. JDK动态代理和CGLIB动态代理的区别&#xff1f; 6. 解释一下什么是 ioc&#xff1f; 7. spring 有哪些主要模块&#xff1f;…

layui框架实战案例(26):layui-carousel轮播组件添加多个Echarts图标的效果

在Layui中&#xff0c;使用layui-carousel轮播组件嵌套Echarts图表来实现多个图表的展示。 css层叠样式表 调整轮播图背景色为白色&#xff1b;调整当个Echarts图表显示loading…状态&#xff1b;同一个DIV轮播项目添加多个Echarts的 .layui-carousel {background-color: #f…

前端路径问题总结

1.相对路径 不以/开头 以当前资源的所在路径为出发点去找目标资源 语法: ./表示当前资源的路径 ../表示当前资源的上一层路径 缺点:不同位置,相对路径写法不同2.绝对路径 以固定的路径作为出发点作为目标资源,和当前资源所在路径没关系 语法:以/开头,不同的项目中,固定的路径…

当前2024阿里云服务器哪个地域价格比较优惠,哪个地域便宜?

目前2024年阿里云服务器地域对比哪个价格更优惠&#xff1f;华北6&#xff08;乌兰察布&#xff09;、华北3&#xff08;张家口&#xff09;、华北1&#xff08;青岛&#xff09;和华南2&#xff08;河源&#xff09;地域更便宜&#xff0c;云服务器吧yunfuwuqiba.com整理阿里云…

Python 网络请求:深入理解Requests库

目录 引言 一、Requests库简介 二、安装与基本使用 三、requests库的特性与优势 四、requests库在实际应用中的案例 1.get请求 2.post请求 3.超时重试 4.headers设置 5.session会话 6.携带cookie​​​​​​​ 7.携带代理​​​​​​​ 8.携带身份认证​​​​​…

SpringBoot学习笔记-S2

1. SpringBoot中的常见注解 RequestBody&#xff1a;使SpringMVC框架可自动读取请求体里面的JSON格式的数据&#xff0c;转换成map类型的集合对象RestController&#xff1a;开发RESTful API 时使用&#xff0c;等价于ResponseBody Controller。RestController和Controller的…