常用设计模式+集成websocket
文章目录
1.模板设计模式
1.超类型
Action.java 行为能力接口
package com.sunxiansheng.designPattern.template;
/**
* Description: 行为能力接口
* @Author sun
* @Create 2024/7/22 10:18
* @Version 1.0
*/
public interface Action {
/**
* 参数校验
*/
void validate();
/**
* 执行
*/
void execute();
/**
* 后续
*/
void after();
}
2.具体模板
ApiTemplate.java 聚合行为能力,不同的行为能力,使得模板有不同的表现
package com.sunxiansheng.designPattern.template;
import com.sunxiansheng.response.Result;
/**
* Description: api模板
* @Author sun
* @Create 2024/7/22 10:17
* @Version 1.0
*/
public class ApiTemplate {
/**
* 执行方法聚合Action的不同对象逻辑,从而实现模板的动态变化
* @param result
* @param action
*/
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.客户端
1.ApiDemo.java
package com.sunxiansheng.designPattern.template;
import com.sunxiansheng.response.Result;
/**
* Description:
* @Author sun
* @Create 2024/7/22 10:23
* @Version 1.0
*/
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;
/**
* Description: 支付策略的类型枚举
* @Author sun
* @Create 2024/7/22 10:54
* @Version 1.0
*/
@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;
}
/**
* 根据code来获取枚举
* @param code
* @return
*/
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;
/**
* Description: 支付策略的能力接口
* @Author sun
* @Create 2024/7/22 10:53
* @Version 1.0
*/
public interface PayHandler {
/**
* 标识自己的类型的能力
* @return
*/
PayChannelEnum getChannel();
/**
* 解决支付的能力
*/
void dealPay();
}
3.具体实现
1.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;
/**
* Description: 银行支付策略
* @Author sun
* @Create 2024/7/22 10:51
* @Version 1.0
*/
@Component
public class BankPayHandler implements PayHandler {
/**
* 标识自己的类型
* @return
*/
@Override
public PayChannelEnum getChannel() {
return PayChannelEnum.BANK_PAY;
}
/**
* 银行支付策略
*/
@Override
public void dealPay() {
System.out.println("银行支付策略");
}
}
2.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;
/**
* Description: 微信支付策略
* @Author sun
* @Create 2024/7/22 10:51
* @Version 1.0
*/
@Component
public class WxPayHandler implements PayHandler {
/**
* 标识自己的类型
* @return
*/
@Override
public PayChannelEnum getChannel() {
return PayChannelEnum.WX_PAY;
}
@Override
public void dealPay() {
System.out.println("微信支付策略");
}
}
3.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;
/**
* Description: 支付宝支付策略
* @Author sun
* @Create 2024/7/22 10:51
* @Version 1.0
*/
@Component
public class ZfbPayHandler implements PayHandler {
/**
* 标识自己的类型
* @return
*/
@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;
/**
* Description: 获取策略对象的工厂
* @Author sun
* @Create 2024/7/22 11:07
* @Version 1.0
*/
@Component
public class PayFactory implements InitializingBean {
/**
* 所有的具体支付策略的对象逻辑会被注入到这里
*/
@Resource
private List<PayHandler> payHandlerList;
/**
* 存储策略标识和具体策略的map
*/
private Map<PayChannelEnum, PayHandler> handlerMap = new HashMap<>();
/**
* 根据枚举的code来获取对应的策略
* @param code
* @return
*/
public PayHandler getHandlerByCode(int code) {
PayChannelEnum byCode = PayChannelEnum.getByCode(code);
return handlerMap.get(byCode);
}
/**
* 在bean初始化之后,这个方法会被调用
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception {
// 将所有的策略从list放到map中,直接通过枚举即可获得对应的策略
for (PayHandler payHandler : payHandlerList) {
handlerMap.put(payHandler.getChannel(), payHandler);
}
}
}
5.客户端
1.ServiceHandler.java 业务层,根据code可以直接执行对应策略
package com.sunxiansheng.designPattern.factoryandstragy;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* Description: 业务层
* @Author sun
* @Create 2024/7/22 12:10
* @Version 1.0
*/
@Service
public class ServiceHandler {
/**
* 依赖存放策略的策略工厂
*/
@Resource
private PayFactory payFactory;
public void dealPay(int code) {
PayHandler handlerByCode = payFactory.getHandlerByCode(code);
handlerByCode.dealPay();
}
}
2.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;
/**
* Description: 测试的controller
* @Author sun
* @Create 2024/7/22 12:15
* @Version 1.0
*/
@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
1.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,如果不写就默认跟父模块的一样 -->
<version>${children.version}</version>
</project>
2.sun-common统一管理子模块
3.删除sun-frame对websocket模块的管理
2.配置依赖
1.sun-dependencies
<spring.websocket.version>2.4.2</spring.websocket.version>
<!-- websocket -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>${spring.websocket.version}</version>
</dependency>
2.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;
/**
* Description: websocket配置类(固定配置)
* @Author sun
* @Create 2024/7/23 10:07
* @Version 1.0
*/
@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;
/**
* Description: websocket鉴权和获取信息
* @Author sun
* @Create 2024/7/23 10:10
* @Version 1.0
*/
@Component
public class WebSocketServerConfig extends ServerEndpointConfig.Configurator {
/**
* 鉴权
* @param originHeaderValue
* @return
*/
@Override
public boolean checkOrigin(String originHeaderValue) {
// 获取request
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
// ============================== 校验逻辑 ==============================
// 校验失败就返回false
// ============================== 校验逻辑 ==============================
return true;
}
/**
* 获取信息
* @param sec
* @param request
* @param response
*/
@Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
Map<String, List<String>> parameterMap = request.getParameterMap();
// 这里从参数中获取名为erp的信息
List<String> erpList = parameterMap.get("erp");
// 如果不为空,就放到UserProperties中,使得socket可以读取
if (!CollectionUtils.isEmpty(erpList)) {
sec.getUserProperties().put("erp", erpList.get(0));
}
}
}
3.sun-demo使用websocket
1.引入依赖
<!-- 引入sun-common-websocket -->
<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
// 设置WebSocket路径,配置创建的WebSocketCheckConfig鉴权配置类
@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;
/**
* 当前会话的唯一标识key
*/
private String erp = "";
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, EndpointConfig conf) throws IOException {
//获取用户信息
try {
// 获取用户配置
Map<String, Object> userProperties = conf.getUserProperties();
// 从用户配置中获取"erp"的用户内容
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);
// 模拟心跳机制:
// 前端可以通过setInterval定时任务每个15秒钟调用一次reconnect函数
// reconnect会通过socket.readyState来判断这个websocket连接是否正常
// 如果不正常就会触发定时连接,每15s钟重试一次,直到连接成功
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);
}
}
}
}
作者:S-X-S