常用设计模式+集成websocket

常用设计模式+集成websocket

文章目录

1.模板设计模式

1.超类型
Action.java 行为能力接口
packagecom.sunxiansheng.designPattern.template;/** * Description: 行为能力接口 * @Author sun * @Create 2024/7/22 10:18 * @Version 1.0 */publicinterfaceAction{/** * 参数校验 */voidvalidate();/** * 执行 */voidexecute();/** * 后续 */voidafter();}
2.具体模板
ApiTemplate.java 聚合行为能力,不同的行为能力,使得模板有不同的表现
packagecom.sunxiansheng.designPattern.template;importcom.sunxiansheng.response.Result;/** * Description: api模板 * @Author sun * @Create 2024/7/22 10:17 * @Version 1.0 */publicclassApiTemplate{/** * 执行方法聚合Action的不同对象逻辑,从而实现模板的动态变化 * @param result * @param action */publicvoidexecute(Result result,finalAction 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
packagecom.sunxiansheng.designPattern.template;importcom.sunxiansheng.response.Result;/** * Description: * @Author sun * @Create 2024/7/22 10:23 * @Version 1.0 */publicclassApiDemo{publicstaticvoidmain(String[] args){ApiTemplate apiTemplate =newApiTemplate();Result result =Result.ok();// 这个模板,根据传入的不同行为,就会有不同的体现 apiTemplate.execute(result,newAction(){@Overridepublicvoidvalidate(){System.out.println("开始校验");}@Overridepublicvoidexecute(){System.out.println("执行");}@Overridepublicvoidafter(){System.out.println("后续执行");}});}}

2.工厂+策略设计模式

1.PayChannelEnum.java 策略标识枚举
packagecom.sunxiansheng.designPattern.factoryandstragy;importlombok.Getter;/** * Description: 支付策略的类型枚举 * @Author sun * @Create 2024/7/22 10:54 * @Version 1.0 */@GetterpublicenumPayChannelEnum{ZFB_PAY(0,"支付宝支付"),WX_PAY(1,"微信支付"),BANK_PAY(2,"银行支付");privateint code;privateString desc;PayChannelEnum(int code,String desc){this.code = code;this.desc = desc;}/** * 根据code来获取枚举 * @param code * @return */publicstaticPayChannelEnumgetByCode(int code){for(PayChannelEnum payChannelEnum :PayChannelEnum.values()){if(payChannelEnum.code == code){return payChannelEnum;}}returnnull;}}
2.超类型
PayHandler.java 策略能力接口
packagecom.sunxiansheng.designPattern.factoryandstragy;/** * Description: 支付策略的能力接口 * @Author sun * @Create 2024/7/22 10:53 * @Version 1.0 */publicinterfacePayHandler{/** * 标识自己的类型的能力 * @return */PayChannelEnumgetChannel();/** * 解决支付的能力 */voiddealPay();}
3.具体实现
1.BankPayHandler.java 银行支付策略
packagecom.sunxiansheng.designPattern.factoryandstragy.handler;importcom.sunxiansheng.designPattern.factoryandstragy.PayChannelEnum;importcom.sunxiansheng.designPattern.factoryandstragy.PayHandler;importorg.springframework.stereotype.Component;/** * Description: 银行支付策略 * @Author sun * @Create 2024/7/22 10:51 * @Version 1.0 */@ComponentpublicclassBankPayHandlerimplementsPayHandler{/** * 标识自己的类型 * @return */@OverridepublicPayChannelEnumgetChannel(){returnPayChannelEnum.BANK_PAY;}/** * 银行支付策略 */@OverridepublicvoiddealPay(){System.out.println("银行支付策略");}}
2.WxPayHandler.java 微信支付策略
packagecom.sunxiansheng.designPattern.factoryandstragy.handler;importcom.sunxiansheng.designPattern.factoryandstragy.PayChannelEnum;importcom.sunxiansheng.designPattern.factoryandstragy.PayHandler;importorg.springframework.stereotype.Component;/** * Description: 微信支付策略 * @Author sun * @Create 2024/7/22 10:51 * @Version 1.0 */@ComponentpublicclassWxPayHandlerimplementsPayHandler{/** * 标识自己的类型 * @return */@OverridepublicPayChannelEnumgetChannel(){returnPayChannelEnum.WX_PAY;}@OverridepublicvoiddealPay(){System.out.println("微信支付策略");}}
3.ZfbPayHandler.java 支付宝支付策略
packagecom.sunxiansheng.designPattern.factoryandstragy.handler;importcom.sunxiansheng.designPattern.factoryandstragy.PayChannelEnum;importcom.sunxiansheng.designPattern.factoryandstragy.PayHandler;importorg.springframework.stereotype.Component;/** * Description: 支付宝支付策略 * @Author sun * @Create 2024/7/22 10:51 * @Version 1.0 */@ComponentpublicclassZfbPayHandlerimplementsPayHandler{/** * 标识自己的类型 * @return */@OverridepublicPayChannelEnumgetChannel(){returnPayChannelEnum.ZFB_PAY;}@OverridepublicvoiddealPay(){System.out.println("支付宝支付策略");}}
4.支付策略工厂
PayFactory.java
packagecom.sunxiansheng.designPattern.factoryandstragy;importorg.springframework.beans.factory.InitializingBean;importorg.springframework.stereotype.Component;importjavax.annotation.Resource;importjava.util.HashMap;importjava.util.List;importjava.util.Map;/** * Description: 获取策略对象的工厂 * @Author sun * @Create 2024/7/22 11:07 * @Version 1.0 */@ComponentpublicclassPayFactoryimplementsInitializingBean{/** * 所有的具体支付策略的对象逻辑会被注入到这里 */@ResourceprivateList<PayHandler> payHandlerList;/** * 存储策略标识和具体策略的map */privateMap<PayChannelEnum,PayHandler> handlerMap =newHashMap<>();/** * 根据枚举的code来获取对应的策略 * @param code * @return */publicPayHandlergetHandlerByCode(int code){PayChannelEnum byCode =PayChannelEnum.getByCode(code);return handlerMap.get(byCode);}/** * 在bean初始化之后,这个方法会被调用 * @throws Exception */@OverridepublicvoidafterPropertiesSet()throwsException{// 将所有的策略从list放到map中,直接通过枚举即可获得对应的策略for(PayHandler payHandler : payHandlerList){ handlerMap.put(payHandler.getChannel(), payHandler);}}}
5.客户端
1.ServiceHandler.java 业务层,根据code可以直接执行对应策略
packagecom.sunxiansheng.designPattern.factoryandstragy;importorg.springframework.stereotype.Service;importjavax.annotation.Resource;/** * Description: 业务层 * @Author sun * @Create 2024/7/22 12:10 * @Version 1.0 */@ServicepublicclassServiceHandler{/** * 依赖存放策略的策略工厂 */@ResourceprivatePayFactory payFactory;publicvoiddealPay(int code){PayHandler handlerByCode = payFactory.getHandlerByCode(code); handlerByCode.dealPay();}}
2.TestController.java 测试的controller
packagecom.sunxiansheng.designPattern.factoryandstragy;importorg.springframework.stereotype.Controller;importorg.springframework.web.bind.annotation.PathVariable;importorg.springframework.web.bind.annotation.RequestMapping;importjavax.annotation.Resource;importjava.util.Objects;/** * Description: 测试的controller * @Author sun * @Create 2024/7/22 12:15 * @Version 1.0 */@ControllerpublicclassPayTestController{@ResourceprivateServiceHandler serviceHandler;@RequestMapping("/payTest/{payType}")publicvoidtest(@PathVariable("payType")String payType){// 根据类型来判断使用哪个策略if(Objects.equals(payType,"支付宝")){ serviceHandler.dealPay(PayChannelEnum.ZFB_PAY.getCode());}elseif(Objects.equals(payType,"微信")){ serviceHandler.dealPay(PayChannelEnum.WX_PAY.getCode());}elseif(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"?><projectxmlns="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(固定配置)
packagecom.sunxiansheng.websocket.config;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.web.socket.server.standard.ServerEndpointExporter;/** * Description: websocket配置类(固定配置) * @Author sun * @Create 2024/7/23 10:07 * @Version 1.0 */@ConfigurationpublicclassWebSocketConfig{@BeanpublicServerEndpointExporterserverEndpointExporter(){returnnewServerEndpointExporter();}}
3.WebSocketServerConfig.java
packagecom.sunxiansheng.websocket.config;importorg.springframework.stereotype.Component;importorg.springframework.util.CollectionUtils;importorg.springframework.web.context.request.RequestContextHolder;importorg.springframework.web.context.request.ServletRequestAttributes;importjavax.servlet.http.HttpServletRequest;importjavax.websocket.HandshakeResponse;importjavax.websocket.server.HandshakeRequest;importjavax.websocket.server.ServerEndpointConfig;importjava.util.List;importjava.util.Map;/** * Description: websocket鉴权和获取信息 * @Author sun * @Create 2024/7/23 10:10 * @Version 1.0 */@ComponentpublicclassWebSocketServerConfigextendsServerEndpointConfig.Configurator{/** * 鉴权 * @param originHeaderValue * @return */@OverridepublicbooleancheckOrigin(String originHeaderValue){// 获取requestServletRequestAttributes servletRequestAttributes =(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();HttpServletRequest request = servletRequestAttributes.getRequest();// ============================== 校验逻辑 ==============================// 校验失败就返回false// ============================== 校验逻辑 ==============================returntrue;}/** * 获取信息 * @param sec * @param request * @param response */@OverridepublicvoidmodifyHandshake(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
packagecom.sunxiansheng.user.websocket;importcom.sunxiansheng.websocket.config.WebSocketServerConfig;importlombok.extern.slf4j.Slf4j;importorg.springframework.stereotype.Component;importjavax.websocket.*;importjavax.websocket.server.ServerEndpoint;importjava.io.IOException;importjava.util.Map;importjava.util.concurrent.ConcurrentHashMap;importjava.util.concurrent.atomic.AtomicInteger;@Slf4j// 设置WebSocket路径,配置创建的WebSocketCheckConfig鉴权配置类@ServerEndpoint(value ="/sys/socket", configurator =WebSocketServerConfig.class)@ComponentpublicclassSysWebSocket{/** * 记录当前在线连接数 */privatestaticAtomicInteger onlineCount =newAtomicInteger(0);/** * 存放所有在线的客户端 */privatestaticMap<String,SysWebSocket> clients =newConcurrentHashMap<>();/** * 与某个客户端的连接会话,需要通过它来给客户端发送数据 */privateSession session;/** * 当前会话的唯一标识key */privateString erp ="";/** * 连接建立成功调用的方法 */@OnOpenpublicvoidonOpen(Session session,EndpointConfig conf)throwsIOException{//获取用户信息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);}}/** * 连接关闭调用的方法 */@OnClosepublicvoidonClose(){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);}}/** * 收到客户端消息后调用的方法 */@OnMessagepublicvoidonMessage(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);}}@OnErrorpublicvoidonError(Session session,Throwable error){ log.error("Socket:{},发生错误,错误原因{}", erp, error.getMessage(), error);try{ session.close();}catch(Exception e){ log.error("onError.Exception{}", e.getMessage(), e);}}/** * 指定发送消息 */publicvoidsendMessage(String message,Session session){ log.info("服务端给客户端[{}]发送消息:{}",this.erp, message);try{ session.getBasicRemote().sendText(message);}catch(IOException e){ log.error("{}发送消息发生异常,异常原因{}",this.erp, message);}}/** * 群发消息 */publicvoidsendMessage(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);}}}}