常用设计模式+集成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);
            }
        }
    }

}
评论 62
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

S-X-S

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值