SpringBoot系统列 5 - 接口版本控制、SpringBoot FreeMarker模板引擎

2023-11-16

 

接着上篇博客的代码继续写

1.接口版本控制

一个系统上线后会不断迭代更新,需求也会不断变化,有可能接口的参数也会发生变化,如果在原有的参数上直接修改,可能会影响线上系统的正常运行,这时我们就需要设置不同的版本,这样即使参数发生变化,由于老版本没有变化,因此不会影响上线系统的运行。

一般我们可以在地址上带上版本号,也可以在参数上带上版本号,还可以再 header 里带上版本号,这里我们在地址上带上版本号,大致的地址如:http://api.example.com/v1/test,其中,v1 即代表的是版本号。具体做法请看代码:

import org.springframework.web.bind.annotation.Mapping;

import java.lang.annotation.*;

/**
 * 版本控制
 * @author XIHONGLEI
 * @date 2018-11-15
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface ApiVersion {
    int value();
}
import org.springframework.web.servlet.mvc.condition.RequestCondition;

import javax.servlet.http.HttpServletRequest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author XIHONGLEI
 * @date 2018-11-15
 */
public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {
    // 路径中版本的前缀, 这里用 /v[1-9]/的形式
    private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile("v(\\d+)/");
    private int apiVersion;

    public ApiVersionCondition(int apiVersion) {
        this.apiVersion = apiVersion;
    }

    @Override
    public ApiVersionCondition combine(ApiVersionCondition other) {
        // 采用最后定义优先原则,则方法上的定义覆盖类上面的定义
        return new ApiVersionCondition(other.getApiVersion());
    }

    @Override
    public ApiVersionCondition getMatchingCondition(HttpServletRequest request) {
        Matcher m = VERSION_PREFIX_PATTERN.matcher(request.getRequestURI());
        if (m.find()) {
            Integer version = Integer.valueOf(m.group(1));
            if (version >= this.apiVersion) {
                return this;
            }
        }
        return null;
    }

    @Override
    public int compareTo(ApiVersionCondition other, HttpServletRequest request) {
        // 优先匹配最新的版本号
        return other.getApiVersion() - this.apiVersion;
    }

    public int getApiVersion() {
        return apiVersion;
    }
}
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.reflect.Method;

/**
 * @author XIHONGLEI
 * @date 2018-11-15
 */
public class CustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
    @Override
    protected RequestCondition<ApiVersionCondition> getCustomTypeCondition(Class<?> handlerType) {
        ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);
        return createCondition(apiVersion);
    }

    @Override
    protected RequestCondition<ApiVersionCondition> getCustomMethodCondition(Method method) {
        ApiVersion apiVersion = AnnotationUtils.findAnnotation(method, ApiVersion.class);
        return createCondition(apiVersion);
    }

    private RequestCondition<ApiVersionCondition> createCondition(ApiVersion apiVersion) {
        return apiVersion == null ? null : new ApiVersionCondition(apiVersion.value());
    }
}
#然后在WebConfig配置类中注入Bean

import com.hello.config.CustomRequestMappingHandlerMapping;
import com.hello.filter.ApiInterceptor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

/**
 * 配置类
 * @author XIHONGLEI
 * @date 2018-10-31
 */
@SpringBootConfiguration
public class WebConfig extends WebMvcConfigurationSupport {

   

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        super.addInterceptors(registry);
        // 将 ApiInterceptor 拦截器类添加进去
        registry.addInterceptor(new ApiInterceptor());
    }

   

    @Override
    @Bean
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
        RequestMappingHandlerMapping handlerMapping = new CustomRequestMappingHandlerMapping();
        handlerMapping.setOrder(0);
        handlerMapping.setInterceptors(getInterceptors());
        return handlerMapping;
    }
}
#最后定义一个带版本控制的接口
import com.hello.WebConfig;
import com.hello.config.ApiVersion;
import com.hello.entity.ContractDetailDto;
import com.hello.service.CheckPositionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController

public class HelloController {

    @Autowired
    private WebConfig webConfig;

    @ApiVersion(1)
    @RequestMapping("{version}/getName")
    public String vGetName() {
        return "Hello World! version 1";
    }
    @ApiVersion(2)
    @RequestMapping("{version}/getName")
    public String getName() {
        return "Hello World! version 2";
    }
}

查看效果:

 

2.模板引擎

在传统的 SpringMVC 架构中,我们一般将 JSP、HTML 页面放到 webapps 目录下面,但是 Spring Boot 没有 webapps,更没有 web.xml,如果我们要写界面的话,该如何做呢?

Spring Boot 官方提供了几种模板引擎:FreeMarker、Velocity、Thymeleaf、Groovy、mustache、JSP。

这里以 FreeMarker 为例讲解 Spring Boot 的使用。

首先引入 FreeMarker 依赖:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>

在 resources 下面建立两个目录:static 和 templates,如图所示:

其中 static 目录用于存放静态资源,譬如:CSS、JS、HTML 等,templates 目录存放模板引擎文件,我们可以在 templates 下面创建一个文件:index.ftl(freemarker 默认后缀为 .ftl),并添加内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello ${name}!</title>
</head>
<body>
        Hello ${name}!
</body>
</html>

然后在POM中配置Resource的时候一定要把所有的资源文件都包括:

<resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.yml</include>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>

新建Contrlller:

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class HomeController {
    @RequestMapping(value = "/index")
    public ModelAndView index() {
        ModelAndView view = new ModelAndView("/index");
        view.addObject("name","Tom");
        return view;
    }
}

看结果:

 

转载于:https://www.cnblogs.com/raphael5200/p/9968057.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SpringBoot系统列 5 - 接口版本控制、SpringBoot FreeMarker模板引擎 的相关文章

  • Java - 如何将特殊字符放入字符串中

    Java 似乎有很好的字符串处理能力 尽管如此 我还是遇到了最简单的问题 我需要动态字符串 它们在运行时更改 因此字符串类型不是一个好的选择 因为它们是不可变的 所以我使用字符数组 设置起来有点痛苦 但至少它们是可以修改的 我想创建一个字符
  • 位图内存不足错误

    我对这个错误有疑问 我从 URL 制作网站图标解析器 我这样做是这样的 public class GrabIconsFromWebPage public static String replaceUrl String url StringB
  • 使用 HttpUrlConnection Android 将 base64 编码的图像发送到服务器

    我正在尝试使用 HttpUrlConnection 将 base64 编码的图像发送到服务器 我遇到的问题是大多数图像均已成功发送 但有些图像会生成 FileNotFound 异常 我的图像编码代码可以在下面找到 public static
  • 手动编辑 Jar 以更改包名称

    我有一个来自外部源的 jar 文件 jar 中的所有类都位于 com xyz 包中 我想将所有类移动到 com xyzold 包中 这是否像解压缩 jar 将 xzy 文件夹重命名为 xyzold 并重新压缩它一样简单 或者我还需要修改每个
  • 使用 Java 在 WebDriver 中按 Ctrl+F5 刷新浏览器

    我已经使用 java 刷新了 WebDriver 中的浏览器 代码如下 driver navigate refresh 如何使用 Java 在 WebDriver 中按 Ctrl F5 来做到这一点 我认为您可以使用 WebDriver 和
  • 无法使用 json 架构验证器根据预定义的 yaml 文件验证查询参数

    我需要根据预定义的 yaml 文件架构验证查询参数的架构 因此我使用 json 架构验证器 验证如何失败 我正在执行以下步骤 填充参数和相应的架构 final List
  • 尝试使用 JRI 将 R 与我的 Java 应用程序集成,但出现错误。谁能解释一下原因和解决办法吗?

    我需要将 Java 与 R 集成来运行一些数学命令并使用 R 的功能进行绘图 以下部分代码给出了错误 public static void main String args HelloRWorld r new HelloRWorld r h
  • java.lang.LinkageError:尝试重复的类定义

    为什么会发生错误以及如何修复它 02 13 02 pool 4 thread 2 WARN Exception in thread pool 4 thread 2 02 13 02 pool 4 thread 2 WARN java lan
  • 正则表达式获取字符串中的第一个数字和其他字符

    我是正则表达式的新手 想知道如何才能只获取字符串中的第一个数字 例如100 2011 10 20 14 28 55 在这种情况下 我希望它返回100 但该数字也可以更短或更长 我在想类似的事情 0 9 但它单独获取每个数字 100 2001
  • 容器中的 JVM 计算处理器错误?

    最近我又做了一些研究 偶然发现了这一点 在向 OpenJDK 团队抱怨之前 我想看看是否有其他人观察到这一点 或者不同意我的结论 因此 众所周知 JVM 长期以来忽略了应用于 cgroup 的内存限制 众所周知 现在从 Java 8 更新某
  • Java-如何将黑白图像加载到二进制中?

    我在 FSE 模式下使用 Java 和 swing 我想将完全黑白图像加载为二进制格式 最好是二维数组 并将其用于基于掩码的每像素碰撞检测 我什至不知道从哪里开始 过去一个小时我一直在研究 但没有找到任何相关的东西 只需将其读入Buffer
  • Jenkins 的代码覆盖率 [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 当您在数组列表上调用remove(object o)时,它如何比较对象?

    当您在 java 中的数组列表上调用remove object o 时 它如何比较对象以找到要删除的正确对象 它使用指针吗 或者它使用 Comparable 接口来比较对象吗 ArrayList remove 依赖于对象的实现Equal方法
  • 在 Java 中将弯音发送到 MIDI 音序器

    我了解启动和运行 MIDI 音序器的基础知识 并且希望能够在播放过程中增加 减小序列的音高 但弯音是发送到合成器而不是音序器的消息 我尝试将音序器的接收器设置为合成器的发射器 当我发送弯音短消息时 音序器保持相同的音调 但随后合成器以新的弯
  • 使用 secp256r1 曲线和 SHA256 算法生成 ECDSA 签名 - BouncyCastle

    我正在尝试使用带有 secp256r1 曲线 P256 的 ECDSA 和用于消息哈希的 SHA256 算法生成签名 我也在使用 Bouncy Castle 库 下面的代码 public class MyTest param args pu
  • Java:由 HTTP 连接创建的等待连接线程存活时间很长

    我有一个服务器端代码 用于检查 SOAP 服务是否已启动 代码如下 String response while response length 0 try final URL url new URL DummySoapServiceURL
  • 使用 JAD 反编译 java - 限制

    我正在尝试使用 Java 中的 JAD 反编译几个 jar 文件 我也尝试过 JD GUI 但运气更差 但出现了很多错误 一种类型 易于修复 似乎是内部类 但我也发现了这段代码 static int SWITCH TABLE atp com
  • 失败时石英重试

    假设我有一个这样配置的触发器
  • 摩尔斯电码 至 英语

    我现在的问题是让 摩尔斯电码转英语 正常工作 将英语转换为莫尔斯电码的第一部分工作正常 我知道以前已经有人问过这个问题 但我不知道我做错了什么 我知道我需要在某个地方进行拆分 但我只是不确定将其放在代码中的何处 现在 莫尔斯电码到英语的部分
  • Java:使用 Graph API 在线更新 Sharepoint 上的 docx 文件

    我在使用 Java 在线更新 Sharepoint 上的 docx 文件时遇到问题 首先 我检查了构建 PUT 请求的 URL 此处 并使用此请求 PUT drives drive id items item id content 我首先使

随机推荐

  • 概率论基础(sigma域、测度)

    一 样本空间 事件 样本空间 Omega 指一个实验的可能结果的集合 omega in Omega 称为 样本
  • 定时任务及分布式定时任务注意事项

    一 定时任务默认是阻塞的 定时任务默认是阻塞的 即串行执行 若一个服务配置多个定时任务 需要等上一个定时任务执行完 才能执行下一个定时任务 一个定时任务超长了 也不应该阻塞其他定时任务的执行 如一个定时任务每秒执行 而业务执行时间是5秒 那
  • vscode (1)直接编译

    第一步创建c文件 第二步配置 终端 配置默认生成任务 选择 usr bin gcc version 2 0 0 tasks type cppbuild label C C gcc 生成活动文件 command usr bin gcc 使用的
  • Stacked Queries(堆叠注入)

    文章目录 基本知识 原理介绍 堆叠注入的局限性 Mysql数据库实例介绍 CTF 实战与各种姿势 修改表名 利用HANDLER语句 利用MySql预处理 正常利用 MySql预处理配合十六进制绕过关键字 MySql预处理配合字符串拼接绕过关
  • jax安装

    Windows 安装 pip install jaxlib 0 3 5 f https whls blob core windows net unstable index html pip install jaxlib cuda111 f
  • Android开发工作中遇到的重点和难点总结

    1 Android N floating widget无法显示 统一管理一个window token解决了此问题 2 Pop up window在Android6 0上出现花屏 3 由于状态栏的影响 悬浮窗上下跳动 4 Wi Fi安全的数据
  • Windows下的oracle 11g的入门

    图全部都挂了 写的太累了 有空再来更 几个月没用oracle之后 花了一个下午把oracle的基本操作迅速捡回来了 记录如下 一 安装oracle11G 1 1 首先要下载oracle服务端和客户端 官网下载链接如下 http www or
  • content-type的几种取值

    目录 Content Type的几种取值 1 text plain 2 text html 3 application json 4 application xml 5 image jpeg 6 image png 7 audio mpeg
  • Socket编程基础

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 前言 一 基于TCP的socket通信流程 二 基于UDP的socket通信流程 三 TCP协议下socket编程主要API接口介绍 1 int socket in
  • 【Java】迭代器之:Iterable & Iterator

    在我们Java中的迭代器是一个接口 名为Iterator 他有两个抽象方法 hasNext方法 用来判断还有没有数据访问 next方法 用来访问集合的下一个数据 迭代器可以访问不同特性的集合数据 而无需关心他们的内部实现 注意 集合并不是直
  • 小学奥数题使用python解决(使用2倒9中不重复的数使得{}+{}={}{}-{}=1{}满足)

    使用2 9中不重复的数使得 1 满足 样子不太好看 1 利用for循环和if语句 代码 利用2 9不重复的数使得 1 i 0 for a1 in range 2 10 for a2 in range 2 10 if a1 a2 and a1
  • 新学期阅读计划

    1 再认真阅读 设计模式之禅 在理解的基础上应用设计模式 2 编程之美 共4章 61个有意思的题目 3 图书馆借阅 算法导论 4 再阅读 算法之道 5 了解 操作系统导论 真正理解不要死记硬背 6 反复多次阅读经典的论文 特别是及时和师姐多
  • 部署篇-Zabbix中文乱码字符集的修正

    部署zabbix监控后默认是英文 默认不支持中文字符集 切换成中文后会出现以下情况 解决方案 从Window服务器找到相应的字休复制到zabbix Server服务器上 控制面板 字体 选择一种中文字体 建议simkai ttf root
  • Java堆和栈应用实例

    以下是一个简单的Java程序 演示了Java堆和栈的应用实例 public class HeapAndStackExample public static void main String args 创建一个对象并分配在堆内存中 Perso
  • CTFshow web入门---web56

    CTFshow web入门 web56 题目 题目分析 查看本题 发现本题为命令执行类题目 但是有一个很致命的点 那么就是他过滤了所有的字母和数字 以及一系列的符号 因此本题最值得推敲的点就是如何实现无字母数字的命令执行 通过拜读P神的一篇
  • 关系型数据库RDBMS -MySQL基础入门(三)数据分片

    数据分片 相关概念 分库分表 分片 存在一台数据库的服务器中的数据 特定方式拆分 分散存放在多台数据库服务中 达到单台服务器负载的效果 垂直分割 纵向切分 按业务类型 什么是垂直分割 纵向切分 把单一的表 拆分成多个表 并分散到不同的数据库
  • 深入理解gtest C/C++单元测试经验谈

    Google C Testing Framework 简称gtest http code google com p googletest 是Google公司发布的一个开源C C 单元测试框架 已被应用于多个开源项目及Google内部项目中
  • spring Data JPA 拾遗

    Preface JPA在国内的使用频率较小 但也是一个值得学习的极为优秀的ORM框架 DDD的思想在里面体现得淋漓尽致 结构图 配置 1 2 3 4 5 6 7 8 9 10 11 spring jpa generate ddl false
  • 搭建jboss

    jboss 是中间件comcat是框架 jboss 基于java需要安装jbk配置环境变量 配置环境变量 我的电脑 右键 属性 高级 环境变量 新建系统变量 变量名为 JAVA HOME 变量值 C Program Files Java j
  • SpringBoot系统列 5 - 接口版本控制、SpringBoot FreeMarker模板引擎

    接着上篇博客的代码继续写 1 接口版本控制 一个系统上线后会不断迭代更新 需求也会不断变化 有可能接口的参数也会发生变化 如果在原有的参数上直接修改 可能会影响线上系统的正常运行 这时我们就需要设置不同的版本 这样即使参数发生变化 由于老版