Spring MVC Web 开发实战:加法计算器、登录与留言板
Spring MVC Web 开发实战涵盖加法计算器、用户登录及留言板功能实现。内容涉及前后端接口约定、Session 会话管理、Lombok 注解简化代码以及 RESTful 接口设计。通过具体案例演示了请求参数接收、响应数据格式定义及服务器端逻辑处理流程。

Spring MVC Web 开发实战涵盖加法计算器、用户登录及留言板功能实现。内容涉及前后端接口约定、Session 会话管理、Lombok 注解简化代码以及 RESTful 接口设计。通过具体案例演示了请求参数接收、响应数据格式定义及服务器端逻辑处理流程。

创建 SpringBoot 项目:引入 Spring Web 依赖,把前端页面放在项目中。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="calc/sum" method="post">
<h1>计算器</h1>
数字 1:<input name="num1" type="text"><br>
数字 2:<input name="num2" type="text"><br>
<input type="submit" value="点击相加">
</form>
</body>
</html>


'约定前后端交互接口'是 Web 开发(尤其是前后端分离模式下)的关键环节。这里的'接口'(又称 API,即 Application Programming Interface)并非 JavaSE 阶段所学的'类与接口',而是应用程序对外提供服务的描述,本质是前后端之间遵循的'应用层协议',用于规范客户端(前端)与服务器(后端)之间的信息交换和任务执行规则。简单来说,它明确了客户端可以给服务器发送哪些 HTTP 请求,以及每种请求预期能获取到什么样的 HTTP 响应 —— 比如客户端发起请求时应使用的路径、请求方式(GET/POST 等)、携带的参数(参数名、类型、是否必传),以及服务器返回响应时的数据格式(如 text/html、application/json)、内容结构等。
在前后端分离的开发模式中,前端与后端代码通常由不同团队负责,为避免开发过程中的混乱和对接矛盾,双方会在开发前提前约定交互接口:一般由服务提供方(后端)编写接口文档,明确上述交互细节,交由服务使用方(前端)确认。接口文档类似应用程序的'操作说明书'。
接口文档一旦确定,需尽量保持稳定,避免随意变更;若因业务需求必须调整,需及时通知对方团队,确保前后端开发节奏一致。这种约定的核心价值在于降低前后端团队的沟通成本,明确各自开发边界,让双方可基于统一标准并行开发,最终保证前后端对接时能顺畅协作,减少因交互规则不明确导致的返工问题。
加法计算器功能,对两个整数进行相加,需要客户端提供参与计算的两个数,服务端返回这两个整数计算的结果。
请求路径:calc/sum
请求方式:GET/POST
接口描述:计算两个整数相加
请求参数:
| 参数名 | 类型 | 是否必须 | 备注 |
|---|---|---|---|
| num1 | Integer | 是 | 参与计算的第一个数 |
| num2 | Integer | 是 | 参与计算的第二个数 |
package com.yang.test1_19_5;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/calc")
@RestController
public class CalController {
@RequestMapping("/sum")
public String sum(Integer num1, Integer num2) {
if (num1 == null || num2 == null) {
return "输入的数据不合法";
}
Integer sum = num1 + num2;
return "计算结果:" + sum;
}
}


创建 SpringBoot 项目:引入 Spring Web 依赖,把前端页面放在项目中。
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>用户登录首页</title>
</head>
<body>
登录人:<span></span>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script>
// TODO: 实现首页逻辑
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<h1>用户登录</h1>
用户名:<input name="userName" type="text"><br>
密码:<input name="password" type="password"><br>
<input type="button" value="登录" onclick="login()">
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
<script>
function login() {
// TODO: 实现登录逻辑
}
</script>
用户登录接口:验证用户名和密码是否正确,并返回给前端。首页:告知前端当前登录用户。如果当前已有用户登录,返回登录的账号,如果没有,返回空。
请求路径:/user/login
请求方式:POST
接口描述:校验账号密码是否正确
| 参数名 | 类型 | 是否必须 | 备注 |
|---|---|---|---|
| userName | String | 是 | 校验的账号 |
| passWord | String | 是 | 校验的密码 |
Content-Type: text/html
响应内容:true //账号密码验证成功
false //账号密码验证失败
请求路径:/user/getLoginUser
请求方式:GET
接口描述:查询当前登录的用户
Content-Type: text/html
响应内容:zhangsan
package com.yang.test1_20_1;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/user")
@RestController
public class UserController {
/**
* 校验用户名和密码是否正确
* @param userName 用户名
* @param password 密码
* @return 正确为 true,错误为 false
*/
@PostMapping("/login")
public Boolean login(String userName, String password, HttpSession session) {
if (!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)) {
return false;
}
// 在开发规范中,代码嵌套最好不要超过 5 层
// 使用 equals 方法时,常量尽量写前面
if ("Bruce".equals(userName) && "12345".equals(password)) {
// 存储 Session
session.setAttribute("loginUser", userName);
return true;
}
return false;
}
@GetMapping("/getLoginUser")
public String getLoginUser(HttpServletRequest request) {
// 参数为 false,获取的 session 为 null 时,不会创建 session
HttpSession request.getSession();
(session != ) {
(String) session.getAttribute();
loginUser;
}
;
}
}

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>留言板</title>
<style>
.container { width: 350px; height: 300px; margin: 0 auto; text-align: center; }
.grey { color: grey; }
.container .row { width: 350px; height: 40px; display: flex; justify-content: space-between; align-items: center; }
.container .row input { width: 260px; height: 30px; }
#submit { width: 350px; height: 40px; background-color: orange; : white; : none; : ; : ; : ; }
留言板
输入后点击提交,会将信息显示下方空白处
谁:
对谁:
说什么:
后端需要提供两个服务:用户输入留言信息之后,后端需要把留言信息保存起来;页面展示时,需要从后端获取到所有的留言信息。
获取全部留言信息,可以用 List 表示,可以用 JSON 来描述这个 List 信息。
GET /message/getList
[ { "from":"黑猫", "to":"白猫", "message":"喵" } ]
当发表新留言时,响应格式也是 JSON 数据。
POST message/publish
{ "from":"黑猫", "to":"白猫", "message":"喵" }
{ "OK":1 }
当我们在进行面向对象编程的时候,IDEA 可以帮我们生成带参数的构造方法、Get/Set 方法等。但是如果我们想要添加其他参数时,修改这些方法就会很不方便。而 Lombok 通过注解在编译时自动生成 Getters、Setters、构造函数、toString 等代码。
Lombok 的工作原理是,当编译器(javac)开始把你的 .java 源码翻译成 .class 字节码时,它会先把代码解析成一种树状结构。Lombok 利用 Java 提供的注解处理器机制,在这个翻译过程中'插队'。它扫描到你写的 @Getter 或 @Data 等注解后,就会直接修改这棵树,把原本不存在的 get、set 方法强行'种'进去。
我们要想使用 Lombok 的这些功能,需要现在 pom.xml 里面引入依赖,同时安装插件 Lombok。
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
<scope>compile</scope>
</dependency>

| 注解 | 作用 |
|---|---|
@Getter | 自动添加 getter 方法 |
@Setter | 自动添加 setter 方法 |
@ToString | 自动添加 toString 方法 |
@EqualsAndHashCode | 自动添加 equals 和 hashCode 方法 |
@NoArgsConstructor | 自动添加无参构造方法 |
@AllArgsConstructor | 自动添加全属性构造方法,顺序按照属性的定义顺序 |
@NonNull | 标记属性不能为 null |
@RequiredArgsConstructor | 自动添加必需属性的构造方法,final + @NonNull 的属性为必需 |
package com.yang.test1_21_1;
import lombok.Data;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
@Data
public class MessageInfo {
private String from;
private String to;
private String message;
}
@RequestMapping("/message")
@RestController
public class MessageController {
List<MessageInfo> messageInfoList = new ArrayList<>();
@PostMapping("/publish")
public Boolean publish(@RequestBody MessageInfo messageInfo) {
if (!StringUtils.hasLength(messageInfo.getFrom()) || !StringUtils.hasLength(messageInfo.getTo()) || !StringUtils.hasLength(messageInfo.getMessage())) {
return false;
}
messageInfoList.add(messageInfo);
return true;
}
@GetMapping("/getList")
public List<MessageInfo> getList() {
return messageInfoList;
}
}



微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online