跳到主要内容SpringMVC 企业级开发实战:制作简单小网页 | 极客日志Java大前端java
SpringMVC 企业级开发实战:制作简单小网页
通过计算器、用户登录、留言板及图书管理系统雏形四个案例,演示了 SpringMVC 前后端交互接口的定义与实现。内容涵盖 GET/POST 请求处理、Session 会话管理、JSON 数据交互以及基础 CRUD 逻辑。重点讲解了 Controller 层编写、参数接收方式及前端 AJAX 调用流程,帮助开发者快速上手企业级 Web 项目开发流程。
imJackJia5.3K 浏览 引言
经过了前面几节课夯实的基础,相信大家对于使用 SpringMVC 进行开发有了深刻的理解,一定迫不及待想要自己上手开发独属于自己的小项目了!这节课我将带着大家从零开始做一些简单的小网页。
前后端交互接口
概念理解
接口又叫 API,一般来说接口和 API 指的都是同一个东西。是指应用程序对外提供的服务的描述,用于交换信息和执行任务(跟 JavaSE 类和接口的接口不一样!)。简单来说就是允许客户端给服务器发送哪些 HTTP 请求,并且每种请求预期获取什么样的 HTTP 响应。
前后端分离开发背景下,前端和后端代码通常由不同团队进行开发。双方在开发前会提前约定好交互的方式。把约定的内容写在文档上,就是'接口文档',可以理解为是应用程序的操作说明书。
例如:玩游戏时,按哪个键会产生什么反应,比如无畏契约按 4 拆包等。这些说明一般都会有一些文档或者网页来进行说明,这些按键就是接口,说明书就是接口文档
在项目开发前,根据需求先约定好前后端交互接口,双方按照接口文档进行开发。
实操
需求分析
以一个简单的计算器为例:加法计算器功能,对两个整数进行相加,需要客户端提供参与计算的两个数,服务端返回这两个整数计算的结果。
接口定义
请求路径:calc/sum
请求方式:GET/POST
接口描述:计算两个整数相加
请求参数

响应数据
Content-Type: text/html
响应内容:计算机计算结果:
相加计算器
前端代码:
(注:咱们作为后端开发程序员,不会前端的话可以复制使用)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
计算器
数字 1:
数字 2:
微信扫一扫,关注极客日志
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
</head>
<body>
<form action="calc/sum" method="post">
<h1>
</h1>
<input name="num1" type="text">
<br>
<input name="num2" type="text">
<br>
<input type="submit" value="点击相加">
</form>
</body>
</html>
package com.bite.spring.demo;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/calc")
public class CalcController {
@RequestMapping("/sum")
public String sum(Integer num1, Integer num2) {
Integer sum = num1 + num2;
return "<h1> 相加的结果是:" + sum + "</h1>";
}
}
输入两个数字后,点击相加,前端会触发提交操作,将这些信息提交并转到 calc/sum 页面。
用户登录
需求:用户输入账号和密码,后端进行校验密码是否正确。
- 如果不正确,前端进行用户告知。
- 如果正确,跳转到首页。首页显示当前登录用户。
- 后续再访问首页,可以获取到登录用户信息。
需求分析
对于后端开发人员而言,不涉及前端页面的展示,只需要提供两个功能:
- 登录页面:通过账号和密码,校验输入的账号密码是否正确,并告知前端。
- 首页:告知前端当前登录用户,如果当前已有用户登录,返回登录的账号,如果没有,返回空。
1. 检验接口
请求路径:/user/login
请求方式:POST
接口描述:校验账号密码是否正确
响应数据
Content-Type:text/html
内容:true/false
2. 查询用户登录接口
请求路径:/user/getLoginUser
请求方式:GET
接口描述:查询当前登录的用户
响应:
Content-Type: text/html
响应内容:zhangsan
代码展示
package com.bite.spring.demo;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import lombok.val;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class LoginController {
@RequestMapping("/login")
public Boolean login(String userName, String password, HttpSession session) {
if (!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)) {
return false;
}
if (userName.equals("admin") && password.equals("admin")) {
session.setAttribute("userName", userName);
return true;
} else {
return false;
}
}
@RequestMapping("/getLoginUser")
public String returnUserName(HttpSession session) {
String userName = (String) session.getAttribute("userName");
return userName;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<h1>用户登录</h1>
用户名:<input name="userName" type="text" id="userName"><br>
密码:<input name="password" type="password" id="password"><br>
<input type="button" value="登录" onclick="login()">
<script src="/jquery-3.7.1.min.js"></script>
<script>
function login() {
$.ajax({
type: "post",
url: "/user/login",
data: {
userName: $("#userName").val(),
password: $("#password").val()
},
success: function(result) {
if (result) {
location.href = "index.html";
} else {
alert("密码错误,请确认");
}
}
});
}
</script>
</body>
</html>
<!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 id="loginUser"></span>
<script src="jquery-3.7.1.min.js"></script>
<script>
$.ajax({
url: "/user/getLoginUser",
type: "get",
success: function(userName) {
$("#loginUser").text(userName);
}
});
</script>
</body>
</html>
- 访问 login.html 界面,输入用户名密码。
- login 使用 ajax 跳转到后端 user/login 接口,接口接收用户名密码数据。
- 检测之后返回检测结果。
- login.html 根据结果判断是否跳转到 index.html 界面。
- 跳转后,输出结果。
小 tips:此处将 userName 存储到 Session 中,这样无需显示传参就可以在 index.html 界面得到 userName 的值了。
留言板
需求
输入留言信息,点击提交。后端把数据存储起来。页面展示输入的信息。
- 提交留言:用户输入留言信息之后,后端需要把留言信息保存起来。
- 展示留言:页面展示时,需要从后端获取到所有的留言信息。
接口 1. 获取全部留言:
GET /message/getList
响应:返回 JSON
接口 2. 发表新留言:
POST /message/publish
请求:body 也是 JSON 格式
代码部分
package com.bite.spring.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/message")
public class MessageController {
private List<MessageInfo> messageInfoList = new ArrayList<>();
@PostMapping(value = "/publish")
public String publish(@RequestBody MessageInfo messageInfo) {
if (!StringUtils.hasLength(messageInfo.getFrom()) || !StringUtils.hasLength(messageInfo.getTo()) || !StringUtils.hasLength(messageInfo.getMessage())) {
return "{\"ok\": 0}";
}
messageInfoList.add(messageInfo);
return "{\"ok\": 1}";
}
@GetMapping("/getList")
public List<MessageInfo> getList() {
return messageInfoList;
}
}
<!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: auto; 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: #f8c203; color: white; border: none; margin:10px; border-radius:5px; font-size:20px; cursor: pointer; }
.message-item { margin:8px 0; padding:5px; border-bottom:1px solid #eee; }
</style>
</head>
<body>
<div class="container">
<h1>留言板</h1>
<p class="grey">输入后点击提交,会将信息显示下方空白处</p>
<div class="row"><span>谁:</span><input type="text" name="" id="from" placeholder="请输入留言人"></div>
<div class="row"><span>对谁:</span><input type="text" name="" id="to" placeholder="请输入接收人"></div>
<div class="row"><span>说什么:</span><input type="text" name="" id="say" placeholder="请输入留言内容"></div>
<input type="button" value="提交" id="submit" onclick="submitMessage()">
<div id="messageList" style="margin-top: 20px;"></div>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script>
loadMessageList();
function loadMessageList() {
$.ajax({
type: "get",
url: "/message/getList",
success: function(messages) {
if (messages && messages.length > 0) {
var finalHtml = "";
for (var m of messages) {
finalHtml += `<div class="message-item">${m.from} 对 ${m.to} 说:${m.message}</div>`;
}
$("#messageList").html(finalHtml);
}
},
error: function(xhr, status, error) {
console.error("加载留言失败:", status, error);
alert("加载留言列表失败,请检查后端服务是否启动");
}
});
}
function submitMessage() {
var from = $('#from').val().trim();
var to = $('#to').val().trim();
var say = $('#say').val().trim();
( === ) {
(); $().(); ;
}
(to === ) {
(); $().(); ;
}
(say === ) {
(); $().(); ;
}
data = { : , : to, : say };
$.({
: ,
: ,
: ,
: .(data),
: () {
(result. === ) {
newMessage = ;
$().(newMessage);
$().(); $().(); $().();
} {
();
}
},
: () {
.(, xhr., error, xhr.);
();
}
});
}
</script>
</body>
</html>
图书管理系统(雏形)
注:目前这个代码只能完成登录和生成图书馆两个简单的功能,但是图书管理系统在接下来的博客中会反复出现。最终,我会做出一个完整的、功能多样的、符合企业级开发的管理系统。
package com.hbu.book;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@RestController
@RequestMapping("book")
public class BookController {
@RequestMapping("/getList")
public List<BookInfo> getList() {
List<BookInfo> books = mockData();
for (BookInfo book : books) {
if (book.getStatus() == 1) {
book.setStatusCN("可借阅");
} else {
book.setStatusCN("不可借阅");
}
}
return books;
}
public List<BookInfo> mockData() {
List<BookInfo> books = new ArrayList<>();
for (int i = 0; i < 5; i++) {
BookInfo book = new BookInfo();
book.setId(i);
book.setBookName("书籍" + i);
book.setAuthor("作者" + i);
book.setCount(i * 5 + 3);
book.setPrice(new BigDecimal(new Random().nextInt(100) + 1));
book.setPublish("出版社" + i);
book.setStatus(1);
books.add(book);
}
return books;
}
}
package com.hbu.book;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")
public Boolean login(String name, String password) {
if (!StringUtils.hasLength(name) || !StringUtils.hasLength(password)) {
return false;
}
if ("admin".equals(name) && "admin".equals(password)) {
return true;
}
return false;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>图书列表展示</title>
<link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="css/list.css">
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/bootstrap.min.js"></script>
<script src="js/jq-paginator.js"></script>
</head>
<body>
<div class="bookContainer">
<h2>图书列表展示</h2>
<div class="navbar-justify-between">
<div>
<button class="btn btn-outline-info" type="button" onclick="location.href='book_add.html'">添加图书</button>
<button class="btn btn-outline-info" type="button" onclick="batchDelete()">批量删除</button>
</div>
</div>
<table>
<thead>
<tr>
<td>选择</td>
<td class="width100">图书 ID</td>
<td>书名</td>
<td>作者</td>
<td>数量</td>
<td>定价</td>
<td>出版社</td>
<td>状态</td>
<td class="width200">操作</td>
</tr>
</thead>
<tbody></tbody>
</table>
<div class="demo"><ul id="pageContainer" class="pagination justify-content-center"></ul></div>
<script>
getBookList();
function getBookList() {
$.ajax({
type: "get",
url: "/book/getList",
success: function(result) {
console.log(result);
if (result != null) {
var finalHtml = "";
for (var book of result) {
finalHtml += '<tr>';
finalHtml += '<td><input type="checkbox" name="selectBook" value="' + book.id + '" id="selectBook" class="book-select"></td>';
finalHtml += '<td>' + book.id + '</td>';
finalHtml += '<td>' + book.bookName + '</td>';
finalHtml += '<td>' + book.author + '</td>';
finalHtml += '<td>' + book.count + '</td>';
finalHtml += '<td>' + book.price + '</td>';
finalHtml += '<td>' + book.publish + '</td>';
finalHtml += '<td>' + book.statusCN + '</td>';
finalHtml += '<td><div class=>';
finalHtml += '修改';
finalHtml += ' 删除 ';
finalHtml += '';
finalHtml += "";
}
$("tbody").html(finalHtml);
}
}
});
}
$("#pageContainer").jqPaginator({
totalCounts: 100,
pageSize: 10,
visiblePages: 5,
currentPage: 1,
first: '首页',
prev: '上一页',
next: '下一页',
last: '最后一页',
page: '{{page}}',
onPageChange: function(page, type) {
console.log("第" + page + "页,类型:" + type);
}
});
function deleteBook(id) {
var isDelete = confirm("确认删除?");
if (isDelete) {
alert("删除成功");
}
}
function batchDelete() {
var isDelete = confirm("确认批量删除?");
if (isDelete) {
var ids = [];
$("input:checkbox[name='selectBook']:checked").each(function() {
ids.push($(this).val());
});
console.log(ids);
alert("批量删除成功");
}
}
</script>
</div>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="css/login.css">
<script type="text/javascript" src="js/jquery.min.js"></script>
</head>
<body>
<div class="container-login">
<div class="container-pic"><img src="pic/computer.png" width="350px"></div>
<div class="login-dialog">
<h3>登陆</h3>
<div class="row"><span>用户名</span><input type="text" name="userName" id="userName" class="form-control"></div>
<div class="row"><span>密码</span><input type="password" name="password" id="password" class="form-control"></div>
<div class="row"><button type="button" class="btn btn-info btn-lg" onclick="login()">登录</button></div>
</div>
</div>
<script src="js/jquery.min.js"></script>
<script>
function login() {
$.ajax({
type: "post",
url: "/user/login",
data: {
name: $("#userName").val(),
password: $("#password").val()
},
success: function(result) {
if (result) {
location.href = "book_list.html";
} else {
alert("账号或密码不正确!");
}
}
});
}
</script>
</body>
</html>


if
from
''
alert
"请输入留言人!"
'#from'
focus
return
if
''
alert
"请输入接收人!"
'#to'
focus
return
if
''
alert
"请输入留言内容!"
'#say'
focus
return
var
from
from
to
message
ajax
type
"post"
url
"/message/publish"
contentType
"application/json"
data
JSON
stringify
success
function
result
if
ok
1
var
`<div class="message-item">${from} 对 ${to} 说:${say}</div>`
"#messageList"
append
'#from'
val
""
'#to'
val
""
'#say'
val
""
else
alert
"留言发布失败,请重试!"
error
function
xhr, status, error
console
error
"提交失败:"
status
responseText
alert
`提交失败!错误码:${xhr.status},请检查后端服务是否启动`
"op"
<a href="book_update.html?bookId=' + book.id + '">
</a>
<a href="javascript:void(0)" onclick="deleteBook(' + book.id + ')">
</a>
</div>
</td>
</tr>
<li class="page-item">
<a class="page-link">
</a>
</li>
<li class="page-item">
<a class="page-link" href="javascript:void(0);">
</a>
</li>
<li class="page-item">
<a class="page-link" href="javascript:void(0);">
</a>
</li>
<li class="page-item">
<a class="page-link" href="javascript:void(0);">
</a>
</li>
<li class="page-item">
<a class="page-link" href="javascript:void(0);">
</a>
</li>