设计实现简述

业务需求里校验环节几乎是必须的,对于复杂的业务还会出现层层校验。为了保证不同校验逻辑,可以做到动态可插拔。同时希望后期的扩展性可以更好。
利用策略模式 + 责任链模式对业务校验设计也可以向上声明一个接口,内部提供好校验方法。然后在执行校验时,以责任链的形式一个一个去校验。再基于Nacos提供的一个配置的动态刷新去实现,指定好整体校验链,需要改变时,只需要去修改Nacos中的配置文件即可。

代码示例

示例背景简介

本次示例背景为:短信平台在发送用户短信前的层层校验。总体包括:校验客户的apikey是否合法、校验请求的ip地址是否是白名单、校验短信的签名、校验短信的模板、校验手机号的格式合法性、校验客户剩余的金额是否充足。

定义校验实体

package com.cyx.sms.domain.vo;

import lombok.Data;
import java.io.Serializable;

/**
 * 发短信的实体类
 */
@Data
public class CheckEsmObj implements Serializable {
    /**
     * 手机号
     */
    private String mobile;

    /**
     * apikey
     */
    private String apikey;

    /**
     * ip
     */
    private String ip;

    /**
     * 短信签名
     */
    private String sign;

    /**
     * 短信模板
     */
    private String template;

    /**
     * 余额
     */
    private String fee;
}

准备校验的父接口&实现类

利用策略模式设计公共接口和各个实现类

父接口

package com.cyx.sms.service;

import com.cyx.sms.domain.vo.CheckEsmObj;

/**
 *
 * 做策略模式的父接口
 */
public interface CheckFilter {

    /**
     * 定义校验统一校验方法名
     * @param checkEsmObj
     */
    void check(CheckEsmObj checkEsmObj);
}

6个校验的实现类

第一个实现类:校验客户的apikey是否合法

package com.cyx.sms.service.impl;

import com.cyx.sms.domain.vo.CheckEsmObj;
import com.cyx.sms.service.CheckFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * 校验客户的apikey是否合法
 */
@Service(value = "apikey")
@Slf4j
public class ApiKeyCheckFilter implements CheckFilter {
    @Override
    public void check(CheckEsmObj checkEsmObj) {
        log.info("【接口模块-校验apikey】   校验"+checkEsmObj.getApikey()+"通过");
    }
}

第二个实现类:校验请求的ip地址是否是白名单

package com.cyx.sms.service.impl;

import com.cyx.sms.domain.vo.CheckEsmObj;
import com.cyx.sms.service.CheckFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * 校验请求的ip地址是否是白名单
 */
@Service(value = "ip")
@Slf4j
public class IPCheckFilter implements CheckFilter {
    @Override
    public void check(CheckEsmObj checkEsmObj) {
        log.info("【接口模块-校验ip】   校验"+checkEsmObj.getIp()+"通过");
    }
}

第三个实现类:校验短信的签名

package com.cyx.sms.service.impl;

import com.cyx.sms.domain.vo.CheckEsmObj;
import com.cyx.sms.service.CheckFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * 校验短信的签名
 */
@Service(value = "sign")
@Slf4j
public class SignCheckFilter implements CheckFilter {
    @Override
    public void check(CheckEsmObj checkEsmObj) {
        log.info("【接口模块-校验签名】   校验"+checkEsmObj.getSign()+"通过");
    }
}

第四个实现类:校验短信的模板

package com.cyx.sms.service.impl;

import com.cyx.sms.domain.vo.CheckEsmObj;
import com.cyx.sms.service.CheckFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * 校验短信的模板
 */
@Service(value = "template")
@Slf4j
public class TemplateCheckFilter implements CheckFilter {
    @Override
    public void check(CheckEsmObj checkEsmObj) {
        log.info("【接口模块-校验模板】   校验"+checkEsmObj.getTemplate()+"通过");
    }
}

第五个实现类:校验手机号的格式合法性

package com.cyx.sms.service.impl;


import com.cyx.sms.domain.vo.CheckEsmObj;
import com.cyx.sms.service.CheckFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * 校验手机号的格式合法性
 */
@Service(value = "mobile")
@Slf4j
public class MobileCheckFilter implements CheckFilter {
    @Override
    public void check(CheckEsmObj checkEsmObj) {
        log.info("【接口模块-校验手机号】   校验"+checkEsmObj.getMobile()+"通过");
    }
}

第六个实现类:校验客户剩余的金额是否充足

package com.cyx.sms.service.impl;


import com.cyx.sms.domain.vo.CheckEsmObj;
import com.cyx.sms.service.CheckFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * 校验客户剩余的金额是否充足
 */
@Service(value = "fee")
@Slf4j
public class FeeCheckFilter implements CheckFilter {
    @Override
    public void check(CheckEsmObj checkEsmObj) {
        log.info("【接口模块-校验客户余额】校验"+checkEsmObj.getFee()+"通过");
    }
}

准备上下文对象

利用责任链模式,实现整个校验链时,直接获取到Context上下文对象即可

package com.cyx.sms.boot;

import com.cyx.sms.domain.vo.CheckEsmObj;
import com.cyx.sms.service.CheckFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * 上下文对象
 * @RefreshScope注解可在不更新代码的情况下,动态刷新nacos配置信息
 */
@Component
@RefreshScope
public class CheckFilterContext {

    // Spring的IOC会将对象全部都放到Map集合中
    // 基于4.x中Spring提供的反省注解,基于Map拿到需要的类型对象即可
    @Autowired
    private Map<String, CheckFilter> checkFiltersMap;

    // 基于Nacos获取到执行的顺序和需要执行的校验对象
    // 未配置时,执行如下默认的校验项和校验顺序
    @Value("${filters:ip,apikey,sign,template,mobile,fee}")
    private String filters;

    /**
     * 当前check方法用于管理校验链的顺序
     */
    public void check(CheckEsmObj checkEsmObj){
        //1. 将获取到filters基于,做切分
        String[] filterArray = filters.split(",");
        //2. 遍历数组即可
        for (String filter : filterArray) {
            CheckFilter checkFilter = checkFiltersMap.get(filter);
            checkFilter.check(checkEsmObj);
        }
    }
}

Nacos配置里准备顺序

# 校验策略的执行内容和顺序 apikey,ip,sign,template,mobile,fee
filters: apikey,ip,sign,template,mobile,fee

接口使用

package com.cyx.sms.controller;

import com.cyx.sms.boot.CheckFilterContext;
import com.cyx.sms.domain.vo.CheckEsmObj;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/checkFilter")
public class TestController
{
    @Autowired
    private CheckFilterContext checkFilterContext;

    /**
     * 校验链
     */
    @PostMapping
    public void testCheckFilter(@RequestBody CheckEsmObj checkEsmObj)
    {
        System.out.println("====================================");
        checkFilterContext.check(checkEsmObj);
    }

}

请求与结果

在这里插入图片描述
在这里插入图片描述

GitHub 加速计划 / na / nacos
123
24
下载
Nacos是由阿里巴巴开源的服务治理中间件,集成了动态服务发现、配置管理和服务元数据管理功能,广泛应用于微服务架构中,简化服务治理过程。
最近提交(Master分支:4 个月前 )
762303b9 * [ISSUE #12970] Fix NamingMetadataManager.removeInstanceMetadata() error fix #12970 * Update NamingMetadataManagerTest.java 4 天前
05561813 * fix type search on mysql model * 灰度模型迁移程序并发&迁移不落历史表 * Config migrate executor times * 1.Config migrate executor times 2. history comparation optimize * 1.Config migrate executor times 2. history comparation optimize * checkstyle 4 天前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐