背景

最近遇到一个技术需求,需要对其他多个已有的服务进行整合打包为一个整体的服务,项目启动过程发现一个问题,在controller层多个服务之间存在相同的RequestMapping接口请求路径,导致服务无法启动。

目前的接口定义规范为:/服务名(context-path)/接口版本号/模块名/接口名

例如通过用户Id查询用户信息的接口,在统一认证服务和用户管理服务有如下接口定义

统一认证服务:/sso/v1.0/user/get/{id}

用户管理服务:/user/v1.0/user/get/{id}

当我们把统一认证服务和用户管理服务进行整合为一个服务时,/v1.0/user/get/{id}请求路径发生重复,导致服务无法启动。

解决方案

为了解决服务之间接口定义冲突的问题,我们准备对接口的请求路径进行动态修改,主要是通过controller类所在的包路径进行服务名的识别,并加入到接口请求路径的最前面。

举个栗子:

统一认证服务controller层伪代码如下:

package cn.codest.sso.web;

@RestController
@Api(tags = "统一认证服务")
@RequestMapping("/v1.0/user")
@RequiredArgsConstructor
public class SSOController { private final UserService userService; @GetMapping("/get/{id}")
@ApiOperation(value = "用户查询", httpMethod = "GET", notes = "用户查询")
public SwaggerResultUtil getById(String id){
return SwaggerResultUtil.resultSuccess(userService.get(id));
} }

通过重载RequestMappingHandlerMapping类的getMappingForMethod方法,实现在项目启动过程中注册/v1.0/user/get/{id}接口时,识别package的路径cn.codest.sso.web提取业务标识sso,修改接口注册地址为/sso/v1.0/user/get/{id},具体代码如下:

@Slf4j
@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
public class PackagePathRequestMappingHandler extends RequestMappingHandlerMapping { private static final String PACKAGE_PREFIX = "cn.codest"; private static final String SERVICE_PREFIX = "service"; private static final String PROVIDER_PREFIX = "providerD"; /**
* 包名对应的服务名缓存类
*/
private final LinkedHashMap<String, String> services = new LinkedHashMap<>(8); @Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { // 判断当前注册的controller接口属于业务层controller,部分中间件例如swagger也会进行接口注册
if (!StringUtils.startsWith(handlerType.getName(), PACKAGE_PREFIX)
|| !AnnotatedElementUtils.hasAnnotation(method, RequestMapping.class)
|| AnnotatedElementUtils.hasAnnotation(handlerType, FeignClient.class)) {
return super.getMappingForMethod(method, handlerType);
} try {
// 构造RequestMapping对象
RequestMappingInfo mapping = super.getMappingForMethod(method, handlerType);
// 根据包路径获取服务名
String serviceName = getServiceName(handlerType.getName());
if (StringUtils.isBlank(serviceName)) {
return mapping;
}
// 增加服务名前缀
return RequestMappingInfo.paths(serviceName).build().combine(mapping);
} catch (Exception e) {
log.error("重写RequestMapping请求路径时发生错误[class: {}, method: {}]", handlerType.getName(), method.getName(), e);
throw e;
}
} protected String getServiceName(String className) {
// 分割类限定名
String[] packages = className.split("\\."); // 判断包路径长度
if (packages.length > 3) {
// 获取子产品包名
String serviceName = packages[2]; // 读取缓存
if (services.containsKey(serviceName)) {
return services.get(serviceName);
} else if (StringUtils.startsWith(serviceName, SERVICE_PREFIX)) { // service服务
services.put(serviceName, serviceName.replace(SERVICE_PREFIX, StringUtils.EMPTY));
} else if (StringUtils.startsWith(serviceName, PROVIDER_PREFIX.toLowerCase())) { // provider服务
services.put(serviceName, serviceName.replace(PROVIDER_PREFIX.toLowerCase(), PROVIDER_PREFIX));
} return services.get(serviceName);
} return StringUtils.EMPTY;
} }

注册自定义RequestMappingHandler,项目使用的SpringBoot版本为2.0.x,不同版本注册方式不同,可以自行查阅官方文档。

public class CustomWebMvcConfig implements WebMvcRegistrations {

    @Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
RequestMappingHandlerMapping handlerMapping = new PackagePathRequestMappingHandler();
handlerMapping.setOrder(Ordered.HIGHEST_PRECEDENCE);
return handlerMapping;
} }

SpringBoot项目启动过程动态修改接口请求路径的更多相关文章

  1. springboot 项目启动后访问不论什么请求的是spring的注册页面Please sign in Username || springboot禁用security

    解决方法: 1.在启动类上添加注解@EnableAutoConfiguration(exclude = {SecurityAutoConfiguration.class}) 2.或者:@SpringB ...

  2. SpringBoot项目启动后再请求远程接口的实现方式

    场景 有一个SpringBoot项目需要在启动后请求另一个远程服务拿取配置,而不是加载过程中去请求,可能会出现类没有实例化的场景,因此需要实现项目完全启动后再进行请求的场景. 解决 一般会有两种实现方 ...

  3. Spring Boot启动过程及回调接口汇总

    Spring Boot启动过程及回调接口汇总 链接: https://www.itcodemonkey.com/article/1431.html 来自:chanjarster (Daniel Qia ...

  4. ssm框架中,项目启动过程以及web.xml配置详解

    原文:https://blog.csdn.net/qq_35571554/article/details/82385838 本篇主要在基于SSM的框架,深入讲解web.xml的配置 web.xml   ...

  5. Springboot 项目启动后执行某些自定义代码

    Springboot 项目启动后执行某些自定义代码 Springboot给我们提供了两种"开机启动"某些方法的方式:ApplicationRunner和CommandLineRun ...

  6. SpringBoot源码分析之SpringBoot的启动过程

    SpringBoot源码分析之SpringBoot的启动过程 发表于 2017-04-30   |   分类于 springboot  |   0 Comments  |   阅读次数 SpringB ...

  7. springboot项目启动成功后执行一段代码的两种方式

    springboot项目启动成功后执行一段代码的两种方式 实现ApplicationRunner接口 package com.lnjecit.lifecycle; import org.springf ...

  8. springboot项目启动之后初始化自定义配置类

    前言 今天在写项目的时候,需要再springboot项目启动之后,加载我自定义的配置类的一些方法,百度了之后特此记录下. 正文 方法有两种: 1. 创建自定义类实现 CommandLineRunner ...

  9. springBoot项目启动类启动无法访问

    springBoot项目启动类启动无法访问. 网上也查了一些资料,我这里总结.下不来虚的,也不废话. 解决办法: 1.若是maven项目,则找到右边Maven Projects --->Plug ...

  10. SpringBoot项目启动时链接数据库很慢

    SpringBoot项目启动时链接数据库很慢 springboot项目在启动时候,如下图所示,链接数据库很慢 解决方法:在mysql 的配置文件中 配置 skip-name-resolve

随机推荐

  1. 【Windows】KMS 激活命令记录

    目录 KMS 服务器激活 Office.Visio 推荐使用 office tool plus 部署并配置 KMS 激活 什么是 KMS? KMS 正版与否的区别 总结 KMS 服务器激活 利用 KM ...

  2. WPF学习 - 动画基础(2)

    上一篇文章粗略的介绍了一下Animation类.本篇介绍一下Storyboard. Storyboard,姑且翻译成"故事板"吧.实际上它是一个Animation对象的容器,可以容 ...

  3. CEMS大学生综合测评管理系统

    功能介绍 登录 首页 修改密码 提交申请 提交列表 数据可视化 审核列表 前端 components结构 搭建Vue项目 ​ Vue3快速上手: ​ https://cn.vuejs.org/guid ...

  4. Spring Bean 的作用域(Bean Scope)

    前言 大家好,我是 god23bin,今天我们来聊一聊 Spring 框架中的 Bean 作用域(Scope). 什么是 Bean 的作用域? 我们在以 XML 作为配置元数据的情况下,进行 Bean ...

  5. RocketMQ 系列(五)高可用与负载均衡

    RocketMQ 系列(五)高可用与负载均衡 RocketMQ 前面系列文章如下: RocketMQ系列(一) 基本介绍 RocketMQ 系列(二) 环境搭建 RocketMQ 系列(三) 集成 S ...

  6. Microsoft Build 2021大会开始后,Develop Blog一系列更新

    .NET BLOG 发布.NET 6预览版4 https://devblogs.microsoft.com/dotnet/announcing-net-6-preview-4/ 发布.NET MAUI ...

  7. Solution -「ARC 123F」Insert Addition

    大约是翻译了一下官方题解? @Description@ 对于一个整数序列 \(P=(P_{1},\dots,P_{m})\),定义 \(f(P)\) 为一个序列 \(Q\) 满足: \(Q_{i}=P ...

  8. Couchdb-权限绕过--命令执行--(CVE-2017-12635)&&(CVE-2017-12636)--H2database命令执行--(CVE-2022-23221)

    Couchdb-权限绕过--命令执行--(CVE-2017-12635)&&(CVE-2017-12636)--H2database命令执行--(CVE-2022-23221) 环境概 ...

  9. Java新特性中的Preview功能如何运行和调试

    在每个Java新版本发布的特性中,都会包含一些Preview(预览)功能,这些功能主要用来给开发者体验并收集建议.所以,Preview阶段的功能并不是默认开启的. 如果想体验某个Java版本中的Pre ...

  10. Go 多版本管理工具

    Go 多版本管理工具 目录 Go 多版本管理工具 一.go get 命令 1.1 使用方法: 二.Goenv 三.GVM (Go Version Manager) 四.voidint/g 4.1 安装 ...