直接通过ip访问网站/做网站需要什么技术
摘要
最近阅读《贯彻设计模式》这本书,里面使用一个更真实的项目来介绍设计模式的使用,相较于其它那些只会以披萨、厨师为例的设计模式书籍是有些进步。但这书有时候为了使用设计模式而强行朝着对应的 UML 图来设计类结构,并且对设计理念缺少讲解,所以也不能说有多优秀,79分的水平。
书中就这部分内容设计,提到使用了:策略模式、门面模式、策略工厂模式、享元模式。但可能真正称得上是设计的内容就两个部分,策略模式和策略工厂模式。但是就书中所写的策略工厂,个人认为有些啰嗦,并且指定全类名,通过反射来获取对象,这种实现不够优雅。个人相信的设计理念就是在实现代码可扩展的前提下,尽可能使用少的类,只开放必要的接口。虽然 Spring 获取 Bean 本质上也是通过反射来创建的,效率并没有提高。但本文设计并不依赖具体框架,基于 Spring 的目的也是和该书一样,为了让案例更接近现实,基于 Spring 既是一种便利,也是一种约束。
本文的设计方案大体总结如下:
- 具体的支付策略实现类(支付宝支付、微信支付)和支付策略门面(Facade)共同实现支付接口
- 通过枚举类定义支付策略,并实现编号到实现类的映射。由于实现类是交给 Spring 管理,所以只需要实现编号到 beanName 的映射
- 在 Facade 中使用 Map 来实现从编号到实现类对象的映射,根据 Bean 的生命周期,在初始化过程中为 Map 赋值。之所以使用 @PostConstruct 注解,是为了让 init 方法作为 private 方法,而 Facade 只需要暴露上层服务真正需要调用的接口方法就行。
基础环境
pom 依赖
<dependency><groupId>com.alipay.sdk</groupId><artifactId>alipay-sdk-java</artifactId><version>4.34.0.ALL</version>
</dependency>
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.7.4</version>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><version>2.7.4</version>
</dependency>
常量工具类
/*** 根据阿里开放平台给出的文档,字段为为第三方平台要求*/
public class AliPayConstant {public static final String OUT_TRADE_NO = "out_trade_no";public static final String TOTAL_AMOUNT = "total_amount";public static final String SUBJECT = "subject";public static final String PRODUCT_CODE = "product_code";public static final String FAST_INSTANT_TRADE_PAY = "FAST_INSTANT_TRADE_PAY";
}
实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@ToString(callSuper = true)
@EqualsAndHashCode
@Builder
public class Order {/*** 订单编号*/private String orderNo;/*** 订单金额*/private Double payment;/*** 订单标题*/private String orderTitle;
}
Spring 配置文件、配置属性类和配置类
payout:alibaba:# 沙箱环境的支付宝网关接口url: https://openapi-sandbox.dl.alipaydev.com/gateway.doapp-id:private-key:alipay-public-key:
@ConfigurationProperties("payout.alibaba")
@Data
public class AlipayProperties {private String url;private String appId;private String privateKey;private String alipayPublicKey;// 默认值private String format = "json";private String charset = "UTF-8";private String signType = "RSA2";
}
@Configuration
@EnableConfigurationProperties(AlipayProperties.class)
public class AlipayConfiguration {@Beanpublic AlipayClient alipayClient(AlipayProperties alipayProperties) throws AlipayApiException {AlipayConfig alipayConfig = new AlipayConfig();//设置网关地址alipayConfig.setServerUrl(alipayProperties.getUrl());//设置应用IDalipayConfig.setAppId(alipayProperties.getAppId());//设置应用私钥alipayConfig.setPrivateKey(alipayProperties.getPrivateKey());//设置请求格式,固定值jsonalipayConfig.setFormat(alipayProperties.getFormat());//设置字符集alipayConfig.setCharset(alipayProperties.getCharset());//设置签名类型alipayConfig.setSignType(alipayProperties.getSignType());//设置支付宝公钥alipayConfig.setAlipayPublicKey(alipayProperties.getAlipayPublicKey());//实例化客户端return new DefaultAlipayClient(alipayConfig);}}
Controller 层
/*** 支付接口,由于要回显html页面,因此直接使用@Controller接口*/
@Controller
@RequestMapping("/payout")
@Slf4j
public class PayoutController {@Autowiredprivate PayoutService payoutService;@SneakyThrows@GetMapping("/{payType}")public void payout(HttpServletResponse response,@PathVariable Integer payType) {log.info("支付方式:{}", payType);Order order = Order.builder().orderNo(UUID.randomUUID().toString()).payment(900.0).orderTitle("兰博基尼").build();String payPageForm = payoutService.pay(order, payType);response.setContentType("text/html;charset=utf-8");PrintWriter out = response.getWriter();out.write(payPageForm);out.flush();out.close();log.info("显示支付页面");}@SneakyThrows@GetMapping("/callback")public void callback(HttpServletResponse response) {log.info("回调页面");PrintWriter out = response.getWriter();out.write("Hello World");out.flush();out.close();log.info("写出消息");}
}
Service 层
@Service
public class PayoutService {@Autowiredprivate PayStrategyFacade payStrategyFacade;public String pay(Order order, Integer payType) {return payStrategyFacade.pay(order, payType);}
}
设计模式部分
策略接口
public interface PayStrategy {/*** 调用第三方支付接口** @param order 订单封装* @return 调用成功返回页面信息,即response.getBody();调用失败返回null*/String pay(Order order);
}
策略实现类(支付宝支付、微信支付、银行支付等)
复制支付宝、微信等开放平台的代码内容即可
@Component("aliPay")
public class AliPayStrategyImpl implements PayStrategy {@Autowiredprivate AlipayClient alipayClient;@Overridepublic String pay(Order order) {// 支付金额double payAmount = order.getPayment();// 订单标题String orderTitle = order.getOrderTitle();// 商户订单号String orderNo = order.getOrderNo();// 不同的请求类型构造不同的Request对象AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();request.setNotifyUrl("");request.setReturnUrl("http://localhost:8080/payout/callback");JSONObject bizContent = new JSONObject();// 必传参数// 商户订单号,商家自定义,保持唯一性bizContent.put(AliPayConstant.OUT_TRADE_NO, orderNo);// 支付金额,最小值0 .01 元bizContent.put(AliPayConstant.TOTAL_AMOUNT, payAmount);// 订单标题,不可使用特殊符号bizContent.put(AliPayConstant.SUBJECT, orderTitle);// 电脑网站支付场景固定传值FAST_INSTANT_TRADE_PAYbizContent.put(AliPayConstant.PRODUCT_CODE, AliPayConstant.FAST_INSTANT_TRADE_PAY);request.setBizContent(bizContent.toString());AlipayTradePagePayResponse response;try {response = alipayClient.pageExecute(request);} catch (AlipayApiException e) {throw new RuntimeException(e);}if (response.isSuccess()) {// 在网站上显示支付宝支付页面,让用户扫描支付return response.getBody();}return null;}
}
策略枚举类
枚举所有的策略,由于枚举类对象是静态对象,因此不能够将其直接作为 Spring 容器的 Bean。
最开始枚举类中设计的两个字段分别是 int 类型的 payType 和 PayStrategy 类型的对象,希望通过 ALIBABA(0, new AliPayStrategyImpl())
的方式来初始化枚举类对象。但是由于 AliPayStrategyImpl 中的 AliClient 是交给了 Spring 容器进行管理,而使用 new 方式得到的对象没有经过 Spring,所以其中的 AliClient 为 null。
同时即使将枚举类交给 Spring 管理,其依赖注入也十分麻烦,通过初始化方法去覆盖类对象中的属性,也需要依赖 beanName,且设计丑陋,没有枚举类的优雅。因此直接在枚举类中负责管理 beanName**(在 Spring 框架中,管理了 beanName,就是管理了 BeanDefinition)**
@Getter
public enum PayStrategyEnum {// TODO: 由于实现类依赖了Spring的自动注入来获取AliClient, 因此直接new AlipayStrategyImpl()不会自动注入AliClientALIBABA(0, "aliPay"),WECHAT(1, "wechatPay"),;private final int payType;/*** 枚举类结合Spring的中介产物,根据迪米特法则,不需要对外开放*/private final String beanName;PayStrategyEnum(Integer payType, String beanName) {this.payType = payType;this.beanName = beanName;}
}
策略门面?策略上下文?策略工厂?
@Component
public class PayStrategyFacade {@Autowiredprivate ApplicationContext applicationContext;// 由于只在初始化的时候进行设置,并发读不存在线程安全问题,因此不需要使用ConcurrentHashMapprivate static final Map<Integer, PayStrategy> PAY_STRATEGIES = new HashMap<>(PayStrategyEnum.values().length);@PostConstructprivate void init() {// 初始化策略for (PayStrategyEnum payStrategyEnum : PayStrategyEnum.values()) {PAY_STRATEGIES.put(payStrategyEnum.getPayType(),applicationContext.getBean(payStrategyEnum.getBeanName(), PayStrategy.class));}}public String pay(Order order, Integer payType) {PayStrategy payStrategy = PAY_STRATEGIES.getOrDefault(payType, null);if (payStrategy == null) {throw new RuntimeException("不支持的支付类型");}return payStrategy.pay(order);}
}
相关文章:

基于Spring的枚举类+策略模式设计(以实现多种第三方支付功能为例)
摘要 最近阅读《贯彻设计模式》这本书,里面使用一个更真实的项目来介绍设计模式的使用,相较于其它那些只会以披萨、厨师为例的设计模式书籍是有些进步。但这书有时候为了使用设计模式而强行朝着对应的 UML 图来设计类结构,并且对设计理念缺少…...

基于Linphone android sdk开发Android软话机
1.Linphone简介 1.1 简介 LinPhone是一个遵循GPL协议的开源网络电话或者IP语音电话(VOIP)系统,其主要如下。使用linphone,开发者可以在互联网上随意的通信,包括语音、视频、即时文本消息。linphone使用SIP协议&#…...

[论文分享]TimeDRL:多元时间序列的解纠缠表示学习
论文题目:TimeDRL: Disentangled Representation Learning for Multivariate Time-Series 论文地址:https://arxiv.org/abs/2312.04142 代码地址:暂无 关键要点:多元时间序列,自监督表征学习,分类和预测 摘…...

分享一个好看的vs主题
最近发现了一个很好看的vs主题(个人认为挺好看的),想要分享给大家。 主题的名字叫NightOwl,和vscode的主题颜色挺像的。操作方法也十分简单,首先我们先在最上面哪一行找到扩展。 然后点击管理扩展,再搜索栏…...

什么是云呼叫中心?
云呼叫中心作为一种高效的企业呼叫管理方案,越来越受到企业的青睐,常被用于管理客服和销售业务。那么,云呼叫中心到底是什么? 什么是云呼叫中心? 云呼叫中心是一种基于互联网的呼叫管理系统,与传统的呼叫…...

还在用nvm?来试试更快的node版本管理工具——fnm
前言 📫 大家好,我是南木元元,热衷分享有趣实用的文章,希望大家多多支持,一起进步! 🍅 个人主页:南木元元 目录 什么是node版本管理 常见的node版本管理工具 fnm是什么 安装fnm …...

【Hadoop精讲】HDFS详解
目录 理论知识点 角色功能 元数据持久化 安全模式 SecondaryNameNode(SNN) 副本放置策略 HDFS写流程 HDFS读流程 HA高可用 CPA原则 Paxos算法 HA解决方案 HDFS-Fedration解决方案(联邦机制) 理论知识点 角色功能 元数据持久化 另一台机器就…...

企业需要哪些数字化管理系统?
企业需要哪些数字化管理系统? ✅企业引进管理系统肯定是为了帮助整合和管理大量的数据,从而优化业务流程,提高工作效率和生产力。 ❌但是,如果各个系统之间不互通、无法互相关联数据的话,反而会增加工作量和时间成本…...

【vue】开发常见问题及解决方案
有一些问题不限于 Vue,还适应于其他类型的 SPA 项目。 1. 页面权限控制和登陆验证页面权限控制 页面权限控制是什么意思呢? 就是一个网站有不同的角色,比如管理员和普通用户,要求不同的角色能访问的页面是不一样的。如果一个页…...

飞天使-k8s知识点3-卸载yum 安装的k8s
要彻底卸载使用yum安装的 Kubernetes 集群,您可以按照以下步骤进行操作: 停止 Kubernetes 服务: sudo systemctl stop kubelet sudo systemctl stop docker 卸载 Kubernetes 组件: sudo yum remove -y kubelet kubeadm kubectl…...

ZooKeeper 集群搭建
文章目录 ZooKeeper 概述选举机制搭建前准备分布式配置分布式安装解压缩并重命名配置环境配置服务器编号配置文件 操作集群编写脚本运行脚本搭建过程中常见错误 ZooKeeper 概述 Zookeeper 是一个开源的分布式服务协调框架,由Apache软件基金会开发和维护。以下是对Z…...

Meson:现代的构建系统
Meson是一款现代化、高性能的开源构建系统,旨在提供简单、快速和可读性强的构建脚本。Meson被设计为跨平台的,支持多种编程语言,包括C、C、Fortran、Python等。其目标是替代传统的构建工具,如Autotools和CMake,提供更简…...

【大模型AIGC系列课程 5-2】视觉-语言大模型原理
重磅推荐专栏: 《大模型AIGC》;《课程大纲》 本专栏致力于探索和讨论当今最前沿的技术趋势和应用领域,包括但不限于ChatGPT和Stable Diffusion等。我们将深入研究大型模型的开发和应用,以及与之相关的人工智能生成内容(AIGC)技术。通过深入的技术解析和实践经验分享,旨在…...

震惊!难怪别人家的孩子越来越聪明,原来竟是因为它
前段时间工作调动给孩子换了个新学校,刚开始担心她不能适应新学校的授课方式,但任课老师对她评价很高,夸她上课很专注。 为了训练孩子的专注力,作为家长可没少下功夫,画画,下五子棋等益智游戏的兴趣班没少…...

Linux操作系统(UMASK+SUID+SGID+STICK)
UMASK反掩码 如何查看反掩码:直接在终端窗口运行 umask root用户反掩码:0022 普通用户反掩码:0002 UMASK的作用:确定目录,文件的缺省权限值 以root身份创建目录,观察目录的9位权限值 以root身份创建普通文件…...

Java 中单例模式的常见实现方式
目录 一、什么是单例模式? 二、单例模式有什么作用? 三、常见的创建单例模式的方式 1、饿汉式创建 2、懒汉式创建 3、DCL(Double Checked Lock)双检锁方式创建 3.1、synchronized 同步锁的基本使用 3.2、使用 DCL 中存在的疑…...

【C语言】自定义类型之联合和枚举
目录 1. 前言2. 联合体2.1 联合体类型的声明2.2 联合体的特点2.3 相同成员的结构体和联合体对比2.4 联合体大小的计算2.4 判断当前机器的大小端 3. 枚举3.1 枚举类型的声明3.2 枚举类型的优点3.3 枚举类型的使用 1. 前言 在之前的博客中介绍了自定义类型中的结构体,…...

使用Mosquitto/python3进行MQTT连接
一、简介 MQTT(消息队列遥测传输)是ISO 标准(ISO/IEC PRF 20922)下基于发布/订阅范式的消息协议。它工作在 TCP/IP协议族上,是为硬件性能低下的远程设备以及网络状况糟糕的情况下而设计的发布/订阅型消息协议,为此,它需要一个消息中间件。 …...

JavaWeb笔记之前端开发HTML
一、引言 1.1HTML概念 网页,是网站中的一个页面,通常是网页是构成网站的基本元素,是承载各种网站应用的平台。通俗的说,网站就是由网页组成的。通常我们看到的网页都是以htm或html后缀结尾的文件,俗称 HTML文件。 …...

通过IP地址定位解决被薅羊毛问题
随着互联网的普及,线上交易和优惠活动日益增多,这也为一些不法分子提供了可乘之机。他们利用技术手段,通过大量注册账号或使用虚假IP地址进行异常操作,以获取更多的优惠或利益,这种行为被称为“薅羊毛”。对于企业和平…...

Leetcode 122 买卖股票的最佳时机 II
题意理解: 已知:一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格 如何哪个时间点买入,哪个时间点卖出,多次交易,能够收益最大化 目的:收益最大化 解题思路: 使用贪心…...

音频文件合成
音频文件合成 音频文件合成 http://ffmpeg.org/download.html https://blog.csdn.net/u013314786/article/details/89682800 http://www.360doc.com/content/19/0317/01/10519289_822112563.shtml https://chaijunkun.blog.csdn.net/article/details/116491526?spm1001.210…...

20231220将NanoPC-T4(RK3399)开发板的Android10的SDK按照Rockchip官方挖掘机开发板编译打包刷机之后启动跑飞
20231220将NanoPC-T4(RK3399)开发板的Android10的SDK按照Rockchip官方挖掘机开发板编译打包刷机之后启动跑飞 2023/12/20 17:19 简略步骤:rootrootrootroot-X99-Turbo:~/3TB$ tar --use-compress-programpigz -xvpf rk3399-android-10.git-20210201.tgz rootrootro…...

vivo 容器平台资源运营实践
作者:vivo 互联网服务器团队 - Chen Han 容器平台针对业务资源申请值偏大的运营问题,通过静态超卖和动态超卖两种技术方案,使业务资源申请值趋于合理化,提高平台资源装箱率和资源利用率。 一、背景 在Kubernetes中,容…...

ASP.NET Core面试题之Redis高频问题
🎈🎈在.NET后端开发岗位中,如今也少不了、微服务、分布式、高并发高可用相关的面试题🎈🎈 👍👍本文分享一些整理的Redis高频面试题🎉 👍👍机会都是给有准备…...

【教程】Ubuntu基本软件安装
文章目录 一、搜狗输入法安装二、百度网盘安装三、划词翻译 一、搜狗输入法安装 全网最准确的Ubuntu 20.04 安装搜狗输入法的步骤 二、百度网盘安装 百度云盘for Linux安装教程和体验 三、划词翻译 ubuntu最好用的划词翻译词典:有道词典和GoldenDict...

Jenkins 构建环境指南
目录 Delete workspace before build starts(常用) Use secret text(s) or file(s) (常用) Add timestamps to the Console Output (常用) Inspect build log for published build scans Terminate a …...

基于Go语言的HTTP路由设计与实现
在Go语言的世界里,HTTP路由是一种将HTTP请求映射到相应处理函数的技术。通过路由,我们可以确定当用户发送一个HTTP请求时,应该调用哪个函数来处理该请求。在这个过程中,我们可以使用多种方法来实现路由设计,下面我将以…...

SpringMVC01
SpringMVC 1. 学习⽬标2. 什么叫MVC?3. SpringMVC 框架概念与特点4. SpringMVC 请求流程5. Spring MVC 环境搭建6. URL 地址映射配置7. 参数绑定8. JSON 数据开发JSON普通数组步骤1:pom.xml添加依赖步骤2: 修改配置⽂件步骤3. 注解使⽤ 1. 学习⽬标 2. 什…...

基于Redis限流(aop切面+redis实现“令牌桶算法”)
令牌桶算法属于流量控制算法,在一定时间内保证一个键(key)的访问量不超过某个阈值。这里的关键是设置一个令牌桶,在某个时间段内生成一定数量的令牌,然后每次访问时从桶中获取令牌,如果桶中没有令牌&#x…...