跳到主要内容Java 常用设计模式与 WebSocket 集成实践 | 极客日志JavaPayjava
Java 常用设计模式与 WebSocket 集成实践
综述由AI生成在 Java Spring Boot 项目中应用模板设计模式和工厂策略设计模式的实现方法,包括 API 执行流程控制及多支付渠道(银行、微信、支付宝)的处理。同时详细讲解了 WebSocket 的集成步骤,涵盖环境搭建、依赖配置、鉴权机制以及客户端连接管理与消息收发功能。
极客零度45 浏览 1. 模板设计模式
1. 超类型
Action.java 行为能力接口
package com.sunxiansheng.designPattern.template;
public interface Action {
void validate();
void execute();
void after();
}
2. 具体模板
ApiTemplate.java 聚合行为能力,不同的行为能力使得模板有不同的表现
package com.sunxiansheng.designPattern.template;
import com.sunxiansheng.response.Result;
public class ApiTemplate {
public void execute(Result result, final Action action) {
try {
action.validate();
action.execute();
action.after();
result.setSuccess(true);
result.setCode(200);
} catch (Exception e) {
result.setSuccess(false);
result.setCode(500);
}
}
}
3. 客户端
ApiDemo.java
package com.sunxiansheng.designPattern.template;
import com.sunxiansheng.response.Result;
public class ApiDemo {
public static void main(String[] args) {
ApiTemplate apiTemplate = new ApiTemplate();
Result result = Result.ok();
apiTemplate.execute(result, new Action() {
@Override
public void validate() {
System.out.println("开始校验");
}
@Override
public void execute() {
System.out.println("执行");
}
@Override
public void after() {
System.out.println("后续执行");
}
});
}
}
2. 工厂 + 策略设计模式
1. PayChannelEnum.java 策略标识枚举
package com.sunxiansheng.designPattern.factoryandstragy;
import lombok.Getter;
@Getter
public enum PayChannelEnum {
ZFB_PAY(0, "支付宝支付"),
WX_PAY(1, "微信支付"),
BANK_PAY(2, "银行支付");
private int code;
private String desc;
PayChannelEnum(int code, String desc) {
this.code = code;
this.desc = desc;
}
public static PayChannelEnum getByCode(int code) {
for (PayChannelEnum payChannelEnum : PayChannelEnum.values()) {
if (payChannelEnum.code == code) {
return payChannelEnum;
}
}
return null;
}
}
2. 超类型
PayHandler.java 策略能力接口
package com.sunxiansheng.designPattern.factoryandstragy;
public interface PayHandler {
PayChannelEnum getChannel();
void dealPay();
}
3. 具体实现
BankPayHandler.java 银行支付策略
package com.sunxiansheng.designPattern.factoryandstragy.handler;
import com.sunxiansheng.designPattern.factoryandstragy.PayChannelEnum;
import com.sunxiansheng.designPattern.factoryandstragy.PayHandler;
import org.springframework.stereotype.Component;
@Component
public class BankPayHandler implements PayHandler {
@Override
public PayChannelEnum getChannel() {
return PayChannelEnum.BANK_PAY;
}
@Override
public void dealPay() {
System.out.println("银行支付策略");
}
}
WxPayHandler.java 微信支付策略
package com.sunxiansheng.designPattern.factoryandstragy.handler;
import com.sunxiansheng.designPattern.factoryandstragy.PayChannelEnum;
import com.sunxiansheng.designPattern.factoryandstragy.PayHandler;
import org.springframework.stereotype.Component;
@Component
public class WxPayHandler implements PayHandler {
@Override
public PayChannelEnum getChannel() {
return PayChannelEnum.WX_PAY;
}
@Override
public void dealPay() {
System.out.println("微信支付策略");
}
}
ZfbPayHandler.java 支付宝支付策略
package com.sunxiansheng.designPattern.factoryandstragy.handler;
import com.sunxiansheng.designPattern.factoryandstragy.PayChannelEnum;
import com.sunxiansheng.designPattern.factoryandstragy.PayHandler;
import org.springframework.stereotype.Component;
@Component
public class ZfbPayHandler implements PayHandler {
@Override
public PayChannelEnum getChannel() {
return PayChannelEnum.ZFB_PAY;
}
@Override
public void dealPay() {
System.out.println("支付宝支付策略");
}
}
4. 支付策略工厂
PayFactory.java
package com.sunxiansheng.designPattern.factoryandstragy;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
public class PayFactory implements InitializingBean {
@Resource
private List<PayHandler> payHandlerList;
private Map<PayChannelEnum, PayHandler> handlerMap = new HashMap<>();
public PayHandler getHandlerByCode(int code) {
PayChannelEnum byCode = PayChannelEnum.getByCode(code);
return handlerMap.get(byCode);
}
@Override
public void afterPropertiesSet() throws Exception {
for (PayHandler payHandler : payHandlerList) {
handlerMap.put(payHandler.getChannel(), payHandler);
}
}
}
5. 客户端
ServiceHandler.java 业务层,根据 code 可以直接执行对应策略
package com.sunxiansheng.designPattern.factoryandstragy;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class ServiceHandler {
@Resource
private PayFactory payFactory;
public void dealPay(int code) {
PayHandler handlerByCode = payFactory.getHandlerByCode(code);
handlerByCode.dealPay();
}
}
TestController.java 测试的 controller
package com.sunxiansheng.designPattern.factoryandstragy;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.annotation.Resource;
import java.util.Objects;
@Controller
public class PayTestController {
@Resource
private ServiceHandler serviceHandler;
@RequestMapping("/payTest/{payType}")
public void test(@PathVariable("payType") String payType) {
if (Objects.equals(payType, "支付宝")) {
serviceHandler.dealPay(PayChannelEnum.ZFB_PAY.getCode());
} else if (Objects.equals(payType, "微信")) {
serviceHandler.dealPay(PayChannelEnum.WX_PAY.getCode());
} else if (Objects.equals(payType, "银行")) {
serviceHandler.dealPay(PayChannelEnum.BANK_PAY.getCode());
}
}
}
集成 websocket
1. 环境搭建
1. 创建新模块 sun-common-websocket
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.sunxiansheng</groupId>
<artifactId>sun-common</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>sun-common-websocket</artifactId>
<version>${children.version}</version>
</project>
sun-common 统一管理子模块
删除 sun-frame 对 websocket 模块的管理
2. 配置依赖
sun-dependencies
<spring.websocket.version>2.4.2</spring.websocket.version>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>${spring.websocket.version}</version>
</dependency>
sun-common-websocket
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
</dependencies>
2. 基础应用
1. 目录结构
2. WebSocketConfig.java(固定配置)
package com.sunxiansheng.websocket.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
3. WebSocketServerConfig.java
package com.sunxiansheng.websocket.config;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;
import java.util.List;
import java.util.Map;
@Component
public class WebSocketServerConfig extends ServerEndpointConfig.Configurator {
@Override
public boolean checkOrigin(String originHeaderValue) {
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
return true;
}
@Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
Map<String, List<String>> parameterMap = request.getParameterMap();
List<String> erpList = parameterMap.get("erp");
if (!CollectionUtils.isEmpty(erpList)) {
sec.getUserProperties().put("erp", erpList.get(0));
}
}
}
3. sun-demo 使用 websocket
1. 引入依赖
<dependency>
<groupId>com.sunxiansheng</groupId>
<artifactId>sun-common-websocket</artifactId>
<version>${sun-common-version}</version>
</dependency>
2. SysWebSocket.java
package com.sunxiansheng.user.websocket;
import com.sunxiansheng.websocket.config.WebSocketServerConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
@ServerEndpoint(value = "/sys/socket", configurator = WebSocketServerConfig.class)
@Component
public class SysWebSocket {
private static AtomicInteger onlineCount = new AtomicInteger(0);
private static Map<String, SysWebSocket> clients = new ConcurrentHashMap<>();
private Session session;
private String erp = "";
@OnOpen
public void onOpen(Session session, EndpointConfig conf) throws IOException {
try {
Map<String, Object> userProperties = conf.getUserProperties();
String erp = (String) userProperties.get("erp");
this.erp = erp;
this.session = session;
if (clients.containsKey(this.erp)) {
clients.get(this.erp).session.close();
clients.remove(this.erp);
onlineCount.decrementAndGet();
}
clients.put(this.erp, this);
onlineCount.incrementAndGet();
log.info("有新连接加入:{},当前在线人数为:{}", erp, onlineCount.get());
sendMessage("连接成功", this.session);
} catch (Exception e) {
log.error("建立链接错误{}", e.getMessage(), e);
}
}
@OnClose
public void onClose() {
try {
if (clients.containsKey(erp)) {
clients.get(erp).session.close();
clients.remove(erp);
onlineCount.decrementAndGet();
}
log.info("有一连接关闭:{},当前在线人数为:{}", this.erp, onlineCount.get());
} catch (Exception e) {
log.error("连接关闭错误,错误原因{}", e.getMessage(), e);
}
}
@OnMessage
public void onMessage(String message, Session session) {
log.info("服务端收到客户端 [{}] 的消息:{}", this.erp, message);
if (message.equals("ping")) {
this.sendMessage("pong", session);
}
}
@OnError
public void onError(Session session, Throwable error) {
log.error("Socket:{},发生错误,错误原因{}", erp, error.getMessage(), error);
try {
session.close();
} catch (Exception e) {
log.error("onError.Exception{}", e.getMessage(), e);
}
}
public void sendMessage(String message, Session session) {
log.info("服务端给客户端 [{}] 发送消息:{}", this.erp, message);
try {
session.getBasicRemote().sendText(message);
} catch (IOException e) {
log.error("{}发送消息发生异常,异常原因{}", this.erp, message);
}
}
public void sendMessage(String message) {
for (Map.Entry<String, SysWebSocket> sessionEntry : clients.entrySet()) {
String erp = sessionEntry.getKey();
SysWebSocket socket = sessionEntry.getValue();
Session session = socket.session;
log.info("服务端给客户端 [{}] 发送消息{}", erp, message);
try {
session.getBasicRemote().sendText(message);
} catch (IOException e) {
log.error("{}发送消息发生异常,异常原因{}", this.erp, message);
}
}
}
}
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online