Spring Boot项目中不使用@RequestMapping相关注解,如何动态发布自定义URL路径

news2024/11/15 8:31:34

一、前言

在Spring Boot项目开发过程中,对于接口API发布URL访问路径,一般都是在类上标识@RestController或者@Controller注解,然后在方法上标识@RequestMapping相关注解,比如:@PostMapping@GetMapping注解,通过设置注解属性,发布URL。在某些场景下,我觉得这样发布URL太麻烦了,不适用,有没有什么其他方法自由发布定义的接口呢?答案是肯定的。

二、一般开发流程

按照上面的描述,我们先看一下一般常用的开发代码:

import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
public class TestController {

    @RequestMapping("/test/url")
    public String test(@RequestParam String name, @RequestBody Map<String, Object> map) { // 这里只是方便测试,实际情况下,请勿使用Map作为参数接收
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("hello, ").append(name).append(", receive param:");
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            stringBuilder.append("\n").append("key: ").append(entry.getKey()).append("--> value: ").append(entry.getValue());
        }
        return stringBuilder.toString();
    }

}

测试效果:

在这里插入图片描述

三、自定义URL发布逻辑

参考步骤二的测试截图效果,我们自定义发布一个URL。

1. 新建一个spring boot项目,导入相关依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

2. 修改Controller实现类代码

去掉@RestController@RequestMapping相关注解,示例代码如下:

import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.Map;

// @RestController
@Component
public class TestController {

    //@RequestMapping("/test/url")
    @ResponseBody  // 注意:此注解需要添加,不能少
    public String test(/*@RequestParam*/ String name,/* @RequestBody*/ Map<String, Object> map) { // 这里只是方便测试,实际情况下,请勿使用Map作为参数接收
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("hello, ").append(name).append(", receive param:");
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            stringBuilder.append("\n").append("key: ").append(entry.getKey()).append("--> value: ").append(entry.getValue());
        }
        return stringBuilder.toString();
    }

}

3. 自定义一个事件监听,实现URL发布功能

参考代码如下:

import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.context.WebServerInitializedEvent;
import org.springframework.context.ApplicationListener;
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 java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 注册一个web容器初始化以后的事件监听,注册自定义URL
 */
@Component
public class CustomRegisterUrl implements ApplicationListener<WebServerInitializedEvent> {
    /**
     * 标识事件监听器是否已经注册,避免重复注册
     */
    private volatile AtomicBoolean flag = new AtomicBoolean(false);

    /**
     * 需要发布的地址
     */
    public static final String CUSTOM_URL = "/test/url";

    @Autowired
    private RequestMappingHandlerMapping requestMappingHandlerMapping;


    @Autowired
    private TestController testController;

    @SneakyThrows
    @Override
    public void onApplicationEvent(WebServerInitializedEvent event) {
        if (flag.compareAndSet(false, true)) {
            // 构建请求映射对象
            RequestMappingInfo requestMappingInfo = RequestMappingInfo
                    .paths(CUSTOM_URL) // 请求URL
                    .methods(RequestMethod.POST, RequestMethod.GET) // 请求方法,可以指定多个
                    .build();
            // 发布url,同时指定执行该请求url的具体类变量的的具体方法
            requestMappingHandlerMapping.registerMapping(requestMappingInfo, testController, testController.getClass().getMethod("test", String.class, Map.class));
        }
    }
}

4. 测试效果

同样请求:http://localhost:8080/test/url?name=jack

在这里插入图片描述

可以看到,此时请求效果并不是正常的,存在参数丢失,怎么办呢?

注意:如果请求出现如下错误:

java.lang.IllegalArgumentException: Expected lookupPath in request attribute "org.springframework.web.util.UrlPathHelper.PATH".

可以在application.yaml文件中添加如下内容:

spring:
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher

5. 增加统一请求处理器

为了实现参数可以正常解析,同时方便增加自定义处理逻辑,我们可以增加一个统一的请求处理器,参考示例:

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Map;

@Component
public class CustomHandlerUrl {

    public static final Method HANDLE_CUSTOM_URL_METHOD;

    private static final ObjectMapper OBJECTMAPPER = new ObjectMapper();

    @Autowired
    private TestController testController;

    static {
        // 提前准备好参数对象
        Method tempMethod = null;
        try {
            tempMethod = CustomHandlerUrl.class.getMethod("handlerCustomUrl", HttpServletRequest.class, HttpServletResponse.class);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        HANDLE_CUSTOM_URL_METHOD = tempMethod;
    }

    @ResponseBody
    /**
     *  拦截自定义请求的url,可以做成统一的处理器,这里我只做简单实现,专门处理test
     */
    public Object handlerCustomUrl(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 获取参数 get方式请求参数
        String name = request.getParameter("name");
        // 获取 post方式请求参数
        Map<String, Object> map = OBJECTMAPPER.readValue(request.getInputStream(), Map.class);

        // 执行业务方法
        String result = testController.test(name, map);
        return result;

    }
}

6. 修改事件监听逻辑

修改事件监听逻辑,此时注册URL时,绑定统一处理器就行了。

示例代码:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.context.WebServerInitializedEvent;
import org.springframework.context.ApplicationListener;
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 java.util.concurrent.atomic.AtomicBoolean;

/**
 * 注册一个web容器初始化以后的事件监听,注册自定义URL
 */
@Component
public class CustomRegisterUrl implements ApplicationListener<WebServerInitializedEvent> {
    /**
     * 标识事件监听器是否已经注册,避免重复注册
     */
    private volatile AtomicBoolean flag = new AtomicBoolean(false);

    /**
     * 需要发布的地址
     */
    public static final String CUSTOM_URL = "/test/url";

    @Autowired
    private RequestMappingHandlerMapping requestMappingHandlerMapping;

    @Autowired
    private CustomHandlerUrl customHandlerUrl;


    @Override
    public void onApplicationEvent(WebServerInitializedEvent event) {
        if (flag.compareAndSet(false, true)) {
            // 构建请求映射对象
            RequestMappingInfo requestMappingInfo = RequestMappingInfo
                    .paths(CUSTOM_URL) // 请求URL
                    .methods(RequestMethod.POST, RequestMethod.GET) // 请求方法,可以指定多个
                    .build();
            // 发布url,指定一下url的处理器
            requestMappingHandlerMapping.registerMapping(requestMappingInfo, customHandlerUrl, CustomHandlerUrl.HANDLE_CUSTOM_URL_METHOD);
        }
    }
}

7. 重新测试

在这里插入图片描述

此时,请求可以发现,效果和使用@RestController+@RequestMapping注解就一样了。

四、写在最后

自定义发布URL路径一般情况下很少使用,不过针对特殊url的处理,以及自定义rpc框架发布url时,选择这样处理好了,可以达到出其不意的效果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1490348.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

⭐每天一道leetcode:21.合并两个有序链表(简单;双指针)

⭐今日份题目 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例1 输入&#xff1a;l1 [1,2,4], l2 [1,3,4] 输出&#xff1a;[1,1,2,3,4,4] 示例2 输入&#xff1a;l1 [], l2 [] 输出&#xff1a;[] 示例3 输入…

【机器学习】包裹式特征选择之递归特征添加法

&#x1f388;个人主页&#xff1a;豌豆射手^ &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;机器学习 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进…

Unity 摄像机的深度切换与摄像机画面投影

摄像机可选&#xff1a;透视、正交 正交类似投影&#xff0c;1比1 透视类似人眼&#xff0c;近大远小 摄像机投影 在项目中新建&#xff1a;渲染器纹理 将新建纹理拖动到相机的目标纹理中 新建一个平面&#xff0c;将新建材质组件放到平面中即可。 相机深度切换 使用代…

探索Java开发面试笔记:以听为目的,助力编程技术提升与面试准备

文章目录 一、制作背景介绍二、 Java开发面试笔记&#xff1a;为你的编程之路加速2.1 公众号主题和目标读者群体2.2 为什么面试笔记对于提高编程视野和技术至关重要2.3 親測效率 三、形式案例3.1 文章形式3.2 手机案例3.3 电脑案例 一、制作背景介绍 做公众号的背景&#xff1a…

十九 超级数据查看器 讲解稿 分栏功能

十九 超级数据查看器 讲解稿 分栏功能 点击此处观看视频教程 讲解稿全文: 大家好&#xff0c;这讲介绍一下 &#xff0c;超级数据查看器的分栏功能。 分栏功能设计的初衷是为了让用户同时同地查询两个表格的数据&#xff0c;方便比较&#xff0c;获得更清晰的查询结果 分栏功…

模型优化_XGBOOST学习曲线及改进,泛化误差

代码 from xgboost import XGBRegressor as XGBR from sklearn.ensemble import RandomForestRegressor as RFR from sklearn.linear_model import LinearRegression as LR from sklearn.datasets import load_boston from sklearn.model_selection import train_test_split,c…

如何添加极狐GitLab Runner 信任域名证书

本文作者 徐晓伟 极狐Gitlab Runner 信任实例域名证书&#xff0c;用于注册注册极狐 GitLab Runner。 问题 参见 极狐gitlab-runner-host.md 说明 解决方案是使用颁发给域名 gitlab.test.helm.xuxiaowei.cn 的证书&#xff0c;可以使用自己的域名去各大云厂商免费申请&#…

重学SpringBoot3-yaml文件配置

重学SpringBoot3-yaml文件配置 引言YAML 基本语法YAML 数据类型YAML 对象YAML 数组复合结构标量引用 YAML 文件结构Spring Boot 中的 YAML 配置注意事项总结参考 引言 YAML&#xff08;YAML Ain’t Markup Language&#xff09;是一种常用于配置文件的数据序列化格式&#xff…

Unity3D

一、C# 输入输出 二、三维数学

CSS变量和@property

CSS变量 var() CSS 变量是由CSS作者定义的实体&#xff0c;其中包含要在整个文档中重复使用的特定值。使用自定义属性来设置变量名&#xff0c;并使用特定的 var() 来访问。&#xff08;比如 color: var(--main-color);&#xff09;。 基本用法 CSS变量定义的作用域只在定义该…

搞定国科金 必备王炸新技术!凌恩生物重磅推出微生物单细胞测序产品

单细胞异质性研究如火如荼&#xff0c;但原核生物研究却是个“坎”。 现有常规的原核生物研究&#xff0c;都集中于单菌群落或微生态大群体&#xff0c;只能从宏观角度研究群体状态&#xff0c;而经典的单细胞RNA测序技术无法应用于细菌。 单细胞技术应用于原核生物的几点障碍…

window10 安装配置docker

前言&#xff08;重要&#xff09;&#xff1a;确认window10版本已经更新到最新版 随着时间推移&#xff0c;docker对window版本的支持也在变&#xff0c;截至2024年3月份&#xff0c;支持win10最低版本号&#xff1a;22H2,操作系统最低版本&#xff1a;19045.2965&#xff0c…

学编程怎么样才能更快入手,编程怎么简单易学

学编程怎么样才能更快入手&#xff0c;编程怎么简单易学 一、前言 对于初学编程建议先从简单入手&#xff0c;然后再学习其他复杂的编程语言。 今天给大家分享的中文编程开发语言工具 进度条构件的用法。 编程入门视频教程链接 https://edu.csdn.net/course/detail/39036 …

26、Qt调用.py文件中的函数

一、开发环境 Qt5.12.0 Python3.7.8 64bit 二、使用 新建一个Qt项目&#xff0c;右击项目名称&#xff0c;选择“添加库” 选择“外部库”&#xff0c;点击“下一步” 点击“浏览”&#xff0c;选择Python安装目录下的libs文件夹中的“python37.lib”文件&#xff0c;点击“下…

【Python如何输入工资,五险一金,专项扣除后得出个税和到手工资(2024年最新)】

最近综合所得年度汇算&#xff0c;正好心血来潮算一下到手工资对不对&#xff0c;有些朋友年综合收入也才几万块&#xff0c;结果年综报税时还要补一两万的个税&#xff0c;这主要是因为跳槽后&#xff0c;上家公司的年薪全平均移到了新的公司每个月中&#xff0c;系统的缺陷导…

第16课:如何出版人生第一本书

机会是留给有准备的人的&#xff0c;在网上多输出文章&#xff0c;就会有更好的曝光机会&#xff0c;有可能被潜在的机会捕捉到。 除了不断的写文章&#xff0c;还可以通过书籍封面的投稿信息进行文章投稿&#xff0c;投稿的文章一定要符合要求。 出书从来不是一件简单的事&am…

Spring MVC源码中设计模式——适配器模式

适配器模式介绍 适配器模式&#xff08;Adapter Pattern&#xff09;是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式&#xff0c;它结合了两个独立接口的功能。 应用场景&#xff1a; 1、系统需要使用现有的类&#xff0c;而此类的接口不符合系统的需要…

EdgeX Foundry 边缘物联网中间件平台

文章目录 1.EdgeX Foundry2.平台架构3.平台服务3.1.设备服务3.2.核心服务3.3.支持服务3.4.应用服务3.5.安全服务3.6.管理服务 EdgeX Foundry # EdgeX Foundryhttps://iothub.org.cn/docs/edgex/ https://iothub.org.cn/docs/edgex/edgex-foundry/1.EdgeX Foundry EdgeX Found…

JavaScript快速入门+文档查询【详解】

目录 1. js简介 2.js引入方式 3. JS基础语法(ECMAScript) 4. js函数和事件【js的核心】 5.js对象 6.BOM对象 7.DOM对象 8.案例全选全消 1. js简介 1.什么是js JavaScript&#xff0c;简称js&#xff0c;是web开发中不可缺少的脚本语言&#xff0c;不需要编译就能…

Spring Test 常见错误

前面我们介绍了许多 Spring 常用知识点上的常见应用错误。当然或许这些所谓的常用&#xff0c;你仍然没有使用&#xff0c;例如对于 Spring Data 的使用&#xff0c;&#xff0c;有的项目确实用不到。那么这一讲&#xff0c;我们聊聊 Spring Test&#xff0c;相信你肯定绕不开对…