常用设计模式+集成websocket

文章目录

  • 1.模板设计模式
  • 1.超类型
  • Action.java 行为能力接口
  • 2.具体模板
  • ApiTemplate.java 聚合行为能力,不同的行为能力,使得模板有不同的表现
  • 3.客户端
  • 1.ApiDemo.java
  • 2.工厂+策略设计模式
  • 1.PayChannelEnum.java 策略标识枚举
  • 2.超类型
  • PayHandler.java 策略能力接口
  • 3.具体实现
  • 1.BankPayHandler.java 银行支付策略
  • 2.WxPayHandler.java 微信支付策略
  • 3.ZfbPayHandler.java 支付宝支付策略
  • 4.支付策略工厂
  • PayFactory.java
  • 5.客户端
  • 1.ServiceHandler.java 业务层,根据code可以直接执行对应策略
  • 2.TestController.java 测试的controller
  • 集成websocket
  • 1.环境搭建
  • 1.创建新模块 sun-common-websocket
  • 1.pom.xml
  • 2.sun-common统一管理子模块
  • 3.删除sun-frame对websocket模块的管理
  • 2.配置依赖
  • 1.sun-dependencies
  • 2.sun-common-websocket
  • 2.基础应用
  • 1.目录结构
  • 2.WebSocketConfig.java(固定配置)
  • 3.WebSocketServerConfig.java
  • 3.sun-demo使用websocket
  • 1.引入依赖
  • 2.SysWebSocket.java
  • 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

    物联沃分享整理
    物联沃-IOTWORD物联网 » 常用设计模式+集成websocket

    发表回复