利用SpringBoot项目做一个Mock挡板;基于事件发布动态自定义URL和响应报文

利用,springboot,项目,一个,mock,挡板,基于,事件,发布,动态,自定义,url,响应,报文 · 浏览次数 : 138

小编点评

**Controller** ```java @Component @SuppressWarnings("unchecked") public class TestController { @Autowired private RedisTemplate redisTemplate; @Autowired @Lazy private TestController testController; @Autowired private RequestMappingHandlerMapping requestMappingHandlerMapping; @Value("\"${mp.event.aeskey:4b57e89bac82a797}\"") private String aesKey; private String mockUrls = "MockUrls"; @EventListener public void handleUserRemovedEvent(AddMockInfoEvent event) { String url = event.getUrl(); String redisSaveKeyUrl = AES.encrypt(url, aesKey); Object context = event.getContext(); redisTemplate.opsForValue().setIfAbsent(redisSaveKeyUrl, context, 48, TimeUnit.HOURS); // 获取所有的Mock的URL Set mockUrlsVar = redisTemplate.opsForSet().members(mockUrls); if (CollectionUtils.isEmpty(mockUrlsVar)) { // 准备好加入动态就接口的URL的容器 mockUrlsVar = new HashSet<>(); } else { // 动态添加接口调用| 找出注册的, RequestMappingInfo build = RequestMappingInfo.paths(mockUrlsVar.stream().toArray(String[]::new)).build(); // 准备注销原始接口 requestMappingHandlerMapping.unregisterMapping(build); } try { // 加入新的URL mockUrlsVar.add(url); // 往redis里面注册URL redisTemplate.opsForSet().add(mockUrls, url); // 设置过期时间 redisTemplate.expire(mockUrls, 48, TimeUnit.HOURS); // 动态添加接口调用 RequestMappingInfo requestMappingInfo = requestMappingHandlerMapping.paths(mockUrlsVar.stream().toArray(String[]::new)) .build(); // 指定接口回调的方法 Method addMockInfoByGetMethod = TestController.class.getDeclaredMethod("addMockInfoByRequest"); // 注册新的接口 requestMappingHandlerMapping.registerMapping(requestMappingInfo, testController, addMockInfoByGetMethod); } catch (NoSuchMethodException e) { e.printStackTrace(); } } } ``` **Event Class** ```java @Getter @Setter public class AddMockInfoEvent { private String url; private Object context; public AddMockInfoEvent(String url, Object context) { this.url = url; this.context = context; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public Object getContext() { return context; } public void setContext(Object context) { this.context = context; } } ```

正文

导入SpringbootWEb依赖

  <!--web项目驱动-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${spring-boot-start-version}</version>
        </dependency>

        <!--redis缓存-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

定义一个Controller,指定RESTFUL接口映射

@RestController
@SwaggerScanClass
public class TestController {


    @Autowired
    private RedisTemplate redisTemplate;


    @Value("${mp.event.aeskey:4b57e89bac82a797}")
    private String aesKey;

    private Map<String, Long> mockUrlTimeOut = new ConcurrentHashMap<>();


    /**
     * Description: 使用 Spring Framework 的事件发布机制发布应用程序事件。
     */
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;
	
	
    @PostMapping("/addMockInfo")
    @ApiOperation("添加Mock信息")
    public Resp addMockInfo(@RequestParam("url") String url, @RequestBody Object body) {
        String redisSaveKey = AES.encrypt(url, aesKey);
        Object returnBody = redisTemplate.opsForValue().get(redisSaveKey);
        if (returnBody != null) {
            redisTemplate.opsForValue().setIfAbsent(redisSaveKey, body, 48, TimeUnit.HOURS);
            return Resp.Ok("url 响应报文更新成功" + url);
        }
        applicationEventPublisher.publishEvent(new AddMockInfoEvent(this, Clock.systemDefaultZone(), url, body));
        return Resp.Ok("url添加Mock成功" + url);
    }

    @PostMapping("/addMockInfoTimeOut")
    @ApiOperation("添加Mock信息Url的阻塞时间")
    public Resp addMockInfoTimeOut(@RequestParam("url") String url, @RequestParam("timeOut") Long timeOut) {
        Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(url, timeOut, 48, TimeUnit.HOURS);
        mockUrlTimeOut.put(url, timeOut);
        return aBoolean ? Resp.Ok("url添加Mock成功" + timeOut) : Resp.error("添加Mock信息Url的阻塞时间失败");
    }


    @ApiOperation("Mock动态回调Request")
    public Resp addMockInfoByRequest() {
        ServletRequestAttributes httpRequest = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = httpRequest.getRequest();
        String requestURI = request.getRequestURI();
        String redisSaveKey = AES.encrypt(requestURI, aesKey);
        Object returnBody = redisTemplate.opsForValue().get(redisSaveKey);
        if (returnBody == null) {
            return Resp.error("Mock动态回调Request 没找到报文");
        }
        Long timeOut = mockUrlTimeOut.get(requestURI);
        if (timeOut == null || timeOut == 0) {
            Object redisSaveTimeOut = redisTemplate.opsForValue().get(requestURI);
            if (redisSaveTimeOut != null) {
                timeOut = (Long) redisSaveTimeOut;
                mockUrlTimeOut.put(requestURI, timeOut);
            }
        }
        try {
            if (timeOut == null || timeOut == 0) {
                return Resp.Ok(returnBody);
            }
            Thread.sleep(timeOut);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return Resp.Ok(returnBody);

    }

}

定义一个事件 AddMockInfoEvent

/**
 * @description: 扩展来创建调用的自定义事件:extends ApplicationEvent
 * @author: GuoTong
 * @createTime: 2023-06-06 20:03
 * @since JDK 1.8 OR 11
 **/
public class AddMockInfoEvent extends ApplicationEvent {

    /**
     * Description: Mock使用的地址
     */
    private String url;

    /**
     * Description: Mock使用的地址的响应报文
     */
    private Object context;


    public AddMockInfoEvent(Object source) {
        super(source);
    }

    public AddMockInfoEvent(Object source, Clock clock) {
        super(source, clock);
    }

    public AddMockInfoEvent(Object source, String url, Object context) {
        super(source);
        this.url = url;
        this.context = context;
    }

    public AddMockInfoEvent(Object source, Clock clock, String url, Object context) {
        super(source, clock);
        this.url = url;
        this.context = context;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public Object getContext() {
        return context;
    }

    public void setContext(Object context) {
        this.context = context;
    }
}

监听事件的发生:调用了添加动态URL的Mock接口时发生

import com.baomidou.mybatisplus.core.toolkit.AES;
import com.gton.controller.TestController;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.event.EventListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.util.UrlPathHelper;

import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @description: 使用@EventListener注解;启用注解驱动的配置时,不需要其他配置。我们的方法可以监听多个事件,
 * @author: GuoTong
 * @createTime: 2023-06-06 20:05
 * @since JDK 1.8 OR 11
 **/
@Component
@SuppressWarnings("unchecked")
public class SystemDIYEventPushListener {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * Description: Controller AA
     */
    @Autowired
    @Lazy
    private TestController testController;

    @Autowired
    private RequestMappingHandlerMapping requestMappingHandlerMapping;


    @Value("${mp.event.aeskey:4b57e89bac82a797}")
    private String aesKey;


    private String mockUrls = "MockUrls";

    /**
     * Description:  启用注解驱动监听事件,AddMockInfoEvent
     *
     * @param event
     * @author: GuoTong
     * @date: 2023-06-06 20:10:52
     * @return:void
     */
    @EventListener
    public void handleUserRemovedEvent(AddMockInfoEvent event) {
        String url = event.getUrl();
        String redisSaveKeyUrl = AES.encrypt(url, aesKey);
        Object context = event.getContext();
        redisTemplate.opsForValue().setIfAbsent(redisSaveKeyUrl, context, 48, TimeUnit.HOURS);
        // 获取所有的Mock的URL
        Set<String> mockUrlsVar = redisTemplate.opsForSet().members(mockUrls);
        if (CollectionUtils.isEmpty(mockUrlsVar)) {
            // 准备好加入动态就接口的URL的容器
            mockUrlsVar = new HashSet<>();
        } else {
            // 动态添加接口调用| 找出注册的,
            RequestMappingInfo build = RequestMappingInfo.paths(mockUrlsVar.stream().toArray(String[]::new)).build();
            // 准备注销原始接口
            requestMappingHandlerMapping.unregisterMapping(build);
        }
        try {
            // 加入新的URL
            mockUrlsVar.add(url);
            // 往redis里面注册URL
            redisTemplate.opsForSet().add(mockUrls, url);
            // 设置过期时间
            redisTemplate.expire(mockUrls, 48, TimeUnit.HOURS);
            // 动态添加接口调用
            RequestMappingInfo requestMappingInfo =
                    RequestMappingInfo.
                            paths(mockUrlsVar.stream().toArray(String[]::new))
                            .build();
            // 指定接口回调的方法
            Method addMockInfoByGetMethod = TestController.class.getDeclaredMethod("addMockInfoByRequest");
            // 注册新的接口
            requestMappingHandlerMapping.
                    registerMapping(requestMappingInfo,
                            testController,
                            addMockInfoByGetMethod);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

    }

}

测试

image

响应

image

试验新的Mock挡板是否动态添加成功

image

添加延时响应

image

查看效果

与利用SpringBoot项目做一个Mock挡板;基于事件发布动态自定义URL和响应报文相似的内容:

利用SpringBoot项目做一个Mock挡板;基于事件发布动态自定义URL和响应报文

# 导入SpringbootWEb依赖 ```xml org.springframework.boot spring-boot-starter-web ${spring-boot-start-version} org.springframework.boot spring-boot-starter-

利用SpringBoot+rabbitmq 实现邮件异步发送,保证100%投递成功

在之前的文章中,我们详细介绍了 SpringBoot 整合 mail 实现各类邮件的自动推送服务。 但是这类服务通常不稳定,当出现网络异常的时候,会导致邮件推送失败。 本篇文章将介绍另一种高可靠的服务架构,实现邮件 100% 被投递成功。类似的短信自动发送等服务也大体相同。 一、先来一张流程图 本文

Springboot 使用nacos鉴权的简单步骤

Springboot 使用nacos鉴权的简单步骤 背景 前端时间nacos爆出了漏洞. 因为他的默认token固定,容易被利用. 具体的问题为: QVD-2023-6271 漏洞描述:开源服务管理平台 Nacos 中存在身份认证绕过漏洞, 在默认配 置下未token.secret.key 进行修改

网易面试:SpringBoot如何开启虚拟线程?

虚拟线程(Virtual Thread)也称协程或纤程,是一种轻量级的线程实现,与传统的线程以及操作系统级别的线程(也称为平台线程)相比,它的创建开销更小、资源利用率更高,是 Java 并发编程领域的一项重要创新。 PS:虚拟线程正式发布于 Java 长期支持版(Long Term Suort,LT

Spring Boot Starter 剖析与实践

本文介绍了在没有 Spring Boot 和 Starter 之前,开发人员在使用传统的 Spring XML 开发 Web 应用时需要引用许多依赖,并且需要大量编写 XML 代码来描述 Bean 以及它们之间的依赖关系。也了解了如何利用 SPI 加载自定义标签来加载 Bean 并进行注入。

利用FastAPI和OpenAI-Whisper打造高效的语音转录服务

最近好久没有写博客了,浅浅记录下如何将OpenAI-Whisper做成Web服务吧 介绍 在这篇指导性博客中,我们将探讨如何在Python中结合使用FastAPI和OpenAI-Whisper。OpenAI-Whisper是一个前沿的语音识别模型,而FastAPI是一个高性能的现代Web框架,专

利用Wireshark抓包分析DNS域名解析过程

一、DNS协议概述 DNS协议也可以称为DNS服务,全称是Domain Name System,即域名系统,和HTTP协议一样,也是一个位于应用层的协议(服务),它是基于运输层的UDP协议的。从DNS的名字我们就可以知道,它提供域名映射到IP地址的服务。 二、实验目的 掌握DNS域名解析过程 熟悉D

利用大型语言模型轻松打造浪漫时刻

在这篇文章中,我们介绍了如何利用大型语言模型为情人节营造难忘的氛围。通过上传图片并进行风格转化,我们可以为对方呈现一幅独特的作品,增添浪漫的色彩。同时,借助搜索功能,我们能够轻松获取与情人节相关的信息,为策划活动提供更多灵感和建议。

利用英特尔 Gaudi 2 和至强 CPU 构建经济高效的企业级 RAG 应用

检索增强生成 (Retrieval Augmented Generation,RAG) 可将存储在外部数据库中的新鲜领域知识纳入大语言模型以增强其文本生成能力。其提供了一种将公司数据与训练期间语言模型学到的知识分开的方式,有助于我们在性能、准确性及安全隐私之间进行有效折衷。 通过本文,你将了解到英特

利用深度循环神经网络对心电图降噪

具体的软硬件实现点击 http://mcu-ai.com/ MCU-AI技术网页_MCU-AI 我们提出了一种利用由长短期记忆 (LSTM) 单元构建的深度循环神经网络来降 噪心电图信号 (ECG) 的新方法。该网络使 用动态模型 ECG 生成的合成数据进行预训 练,并使用来自 Physionet