Spring Web MVC综合案例
承接上篇文章——Spring Web MVC探秘,在了解Spring Web MVC背后的工作机制之后,我们接下来通过三个实战项目,来进一步巩固一下前面的知识。
一、计算器
效果展示:访问路径:http://127.0.0.1:8080/calc.html

前端代码:(文件名:calc.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>
</head>
<body><!--点击“按钮”后,页面会跳转到calc/sum页面--><form action="calc/sum" method="post"><h1>计算器</h1>数字1:<input name="num1" type="text"><br>数字2:<input name="num2" type="text"><br><!-- 只有<input>标签且具有name属性的内容才会被作为表单参数,传递给后端--><input type="submit" value=" 点击相加 "></form>
</body></html>
后端代码:
package com.example.mvc_test1;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/calc")
public class CalcController {@RequestMapping("/sum")//访问路径=类路径+方法路径:/calc/sumpublic String sum(Integer num1,Integer num2){Integer sum= num1+num2;return "计算的结果是:"+sum;}
}
代码解析:
前端部分:
当用户在num1和num2输入框中输入数字后,点击 “点击相加” 按钮,浏览器会根据<form>标签的action属性("calc/sum")和method属性("post"),将表单数据以POST请求的方式发送到后端对应的/calc/sum路径。
后端部分:
1、后端的CalcController类被@RestController注解标记,表明它是一个处理 RESTful 请求的控制器,并且被@RequestMapping("/calc")映射到/calc路径下。
2、sum方法被@RequestMapping("/sum")注解,意味着它处理/calc/sum路径的请求。
3、当后端发送请求到calc/sum路径时,后端代码从前端参数表单中获取参数num1和num2,赋值给sum方法的两个名称对应的参数num1、num2
4、Integer sum = num1 + num2;:在后端接收到这两个参数后,将它们相加,得到结果sum。
return "计算的结果是:" + sum;:最后,将计算结果以字符串的形式返回给前端,前端会根据响应进行相应的显示或处理。
二、用户登录界面
效果展示:访问路径:http://127.0.0.1:8080/login.html
用户名错误登录:
用户名和密码正确登录:
后端代码:
我们先来分析一下这个”登录界面”的需求,当用户输入用户名和密码之后,后端服务器需要做两件事:
1、判断用户是否进行合法的输入
2、校验用户名和密码是否正确
3、将用户名存入Session中
4、返回用户名给前端,
我们将这四件事封装到两个方法中:login方法和getUser方法
@RestController
@RequestMapping("/user")
public class UserController {//login方法:负责1、2、3件事务的完成@RequestMapping("/login") //访问路径:/user/loginpublic boolean login(String userName,String password,HttpServletRequest request){//当用户输入空字符或者没有输入,返回falseif(!StringUtils.hasLength(userName)||!StringUtils.hasLength(password)){return false;}else if("admin".equals(userName)&&"admin".equals(password)){//校验用户名和密码是否正确//用户名和密码这里都设置为admin//登录成功,将用户名设置到Session中HttpSession session=request.getSession();session.setAttribute("userName",userName);return true;}//其他情况,返回falsereturn false;}//getUser方法,负责第4个事务的完成@RequestMapping("/getUser") //访问路径:user/getUserpublic String getUserName(HttpServletRequest request){HttpSession session=request.getSession();//从请求报文中获取SessionString userName=(String) session.getAttribute("userName");//从Session中获取userNamereturn userName;}
}
注:StringUtils.hasLength()是一个工具方法,用于检查字符串是否有长度(即不为null且长度大于0)。
前端代码:登录页面(login.html) 和登录成功的页面(index.html)
由于这两个页面的实现,需要引入一个js文件,我们先来讲一下如何有引入?
1、js下载文件网址: https://code.jquery.com/jquery-3.7.1.min.js

2、将刚刚下载的js文件,赋值粘贴到static文件夹下面:
登录页面:login.html
<!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> <!-- 引入js文件--><script>function login() {$.ajax({type:"post",//请求类型url:"/user/login",//访问后端服务器的user/logindata:{//获取标签的值,赋值给后端指定的参数//左边的usrName表示后端/user/login 方法中的参数userName//右边userName表示id为userName的标签"userName":$("#userName").val(),//val()获取标签的值"password":$("#password").val()},success:function (result){//访问成功,后端返回一个结果,作为function的参数result//访问/user/login时,后端会将这个请求交给login方法处理,处理结果返回true或false,表示登录成功与否if(result==true){//用户名和密码正确,后端返回truelocation.href="index.html";}else{alert("用户名或者密码错误");}}});}</script>
</body></html>
登录成功的页面:index.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> <!-- 引入js文件--><script>$.ajax({type:"get",url:"/user/getUser",success:function (userName){//访问成功,后端返回的结果作为function方法的参数//访问user/getUser时,后端会将这个请求交给getUser方法处理,结果返回用户名$("#loginUser").text(userName);}});</script>
</body></html>
前端代码解析:
或许你有一个疑问,用户在输入用户名和密码后,校验工作是在后端中的login方法完成的,那么校验完成后,前端是如何获取后端检验后的结果呢?这其实借助了一个技术——Ajax。
Ajax(Asynchronous JavaScript and XML)即异步 JavaScript 和 XML,是一种创建快速动态网页的技术。
这里截取了login.html前端代码中使用到Ajax的部分代码(省略注释版):
$.ajax({type: "post", url: "/user/login", data: {"userName": $("#userName").val(), "password": $("#password").val()},success: function (result) {if (result == true) {location.href = "index.html";} else {alert("用户名或者密码错误");}}
});
1、 $.ajax({...}):这是 jQuery 的 ajax 函数,用于发起HTTP 请求。
2、type: "post":指定请求的类型为 POST。这意味着会向服务器发送一个 POST 请求。
3、url: "/user/login":指定请求的 URL,即要发送请求的后端服务地址,这里会将请求发送到 /user/login 路径。
4、data:这是要发送给服务器的数据,是一个对象。"userName": $("#userName").val():使用 jQuery 的 $("#userName") 选择器选中 id 为 userName 的元素,并通过 val() 方法获取其值,将其赋值给 userName 属性。
5、"password": $("#password").val():同理,获取 id 为 password 的元素的值,并将其赋值给 password 属性。
6、success: function (result) {...}:定义请求成功时的回调函数。result:当请求成功后,服务器返回的数据将作为 result 参数传递给该回调函数。
7、if (result == true) {...} else {...}:根据服务器返回的结果进行不同的操作。location.href = "index.html";:如果结果为 true,说明登录成功,将页面重定向到 index.html。alert("用户名或者密码错误");:如果结果不为 true,弹出一个警告框,提示用户用户名或密码错误。
三、留言板
效果展示:访问路径:http://127.0.0.1:8080/messagewall.html

后端代码:
让我们来分析一下这个“留言板”,首先,在打开这个留言板的时候,留言板会加载历史的留言记录,然后,在用户添加新留言之后,点击“提交”,新的留言会添加到页面上。
根据功能,后端代码需要完成两件事:
1、在前端打开这个页面的时候,返回历史留言内容给前端,用于前端加载历史留言内容
2、在用户添加新的留言的时候,将这条新留言存储在后端服务器中
MessageInfo类:
@Data//会自动生成该类的成员的Getter和Setter等方法,需要在pom.xml引入lombok依赖
public class MessageInfo {String from;String to;String say;
}
在pom.xml中引入lombok依赖(@Data注解使用需要)
MessageController类:
@RestController
@RequestMapping("/message")
public class MessageController {List<MessageInfo> list=new ArrayList<>();//存放留言内容//getList方法:返回历史留言内容给前端(访问路径:/message/getList)@RequestMapping("/getList")public List<MessageInfo> getList(){return list;}//publish方法:将用户新添加的留言存在后端服务器中(访问路径:/message/publish)//获取用户输入的内容,添加到list,返回一个json字符串@RequestMapping(value = "/publish",produces = "application/json")//指定该方法返回的数据类型为application/json,表明此方法返回的内容是 JSON 格式。告知前端返回的数据是 JSON 格式。public String publish(@RequestBody MessageInfo message){if(StringUtils.hasLength(message.from)&&StringUtils.hasLength(message.to)&&StringUtils.hasLength(message.say)){list.add(message);return "{\"ok\":1}";//1表示添加成功}return "{\"ok\":0}";//0表示添加失败}
}
前端代码: messagewall.html
<!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;/* border: 1px black solid; */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;color: white;border: none;margin: 10px;border-radius: 5px;font-size: 20px;}</style>
</head><body><div class="container"><h1>留言板</h1><p class="grey">输入后点击提交, 会将信息显示下方空白处</p><div class="row"><span>谁:</span> <input type="text" name="" id="from"></div><div class="row"><span>对谁:</span> <input type="text" name="" id="to"></div><div class="row"><span>说什么:</span> <input type="text" name="" id="say"></div><input type="button" value="提交" id="submit" onclick="submit()"><!-- <div>A 对 B 说: hello</div> --></div><script src="jquery-3.7.1.min.js"></script> <!-- 引入js文件--><script>//加载历史留言记录$.ajax({type:"get",url:"/message/getList",success:function (list){for(let msg of list){console.log(1);let divE = "<div>"+msg.from +"对" +msg.to + "说:" + msg.say+"</div>";//拼接留言$(".container").append(divE);//把留言内容添加到页面上}}});function submit(){//获取留言的内容var from = $('#from').val();var to = $('#to').val();var say = $('#say').val();if (from== '' || to == '' || say == '') {return;}//传递留言内容给前端$.ajax({type:"post",url:"/message/publish",contentType:"application/json",data:JSON.stringify({"from":from,"to":to,"say":say}),success:function (result){if(result.ok==1){alert("添加成功")//构造节点let divE = "<div>"+from +"对" + to + "说:" + say+"</div>";//把节点添加到页面上$(".container").append(divE);//清空输入框的值$('#from').val("");$('#to').val("");$('#say').val("");}else{alert("添加失败")}}});}</script>
</body></html>
前端代码解析:
1、注释“传递留言内容给前端部分”代码:
contentType: "application/json":告诉后端,我要传给你一个Json字符串的数据。
data: JSON.stringify({...}):将包含from、to、say的对象转换为 JSON 字符串作为请求数据发送给后端,让后端的publish方法中的message接收。
success:function(result):当后端返回一个json字符串给前端接收时,前端会自动将这个json字符串转换为一个对象,例如这里的publish方法返回的如果是 {“ok”:1} 的json字符串,将其作为参数传送给function时,此时的result就成为了一个包含“ok”属性的对象,其属性值为1。
相关文章:
Spring Web MVC综合案例
承接上篇文章——Spring Web MVC探秘,在了解Spring Web MVC背后的工作机制之后,我们接下来通过三个实战项目,来进一步巩固一下前面的知识。 一、计算器 效果展示:访问路径:http://127.0.0.1:8080/calc.html 前端代码&a…...
微软预测 AI 2025,AI Agents 重塑工作形式
1月初,微软在官网发布了2025年6大AI预测,分别是:AI模型将变得更加强大和有用、AI Agents将彻底改变工作方式、AI伴侣将支持日常生活、AI资源的利用将更高效、测试与定制是开发AI的关键以及AI将加速科学研究突破。 值得一提的是,微…...
lvgl性能调优
LV_USE_PERFORMANCE lvgl_performance 是 LVGL 提供的性能分析工具,可以帮助开发者评估和优化图形库的性能。在一些特定的版本中,lvgl_performance 是一个宏或者工具,用来分析性能瓶颈,特别是图形渲染的效率。 下面是如何使用 l…...
CSS实现实现票据效果 mask与切图方式
一、“切图”的局限性 传统的“切图”简单暴力,但往往缺少适应性。 适应性一般有两种,一是尺寸自适应,二是颜色可以自定义。 举个例子,有这样一个优惠券样式 关于这类样式实现技巧,之前在这篇文章中有详细介绍: CSS 实现优惠券的技巧 不过这里略微不一样的地方是,两个…...
STL--list(双向链表)
目录 一、list 对象创建 1、默认构造函数 2、初始化列表 3、迭代器 4、全0初始化 5、全值初始化 6、拷贝构造函数 二、list 赋值操作 1、赋值 2、assign(迭代器1,迭代器2) 3、assign(初始化列表) 4、assig…...
ZooKeeper 中的 ZAB 一致性协议与 Zookeeper 设计目的、使用场景、相关概念(数据模型、myid、事务 ID、版本、监听器、ACL、角色)
参考Zookeeper 介绍——设计目的、使用场景、相关概念(数据模型、myid、事务 ID、版本、监听器、ACL、角色) ZooKeeper 设计目的、特性、使用场景 ZooKeeper 的四个设计目标ZooKeeper 可以保证如下分布式一致性特性ZooKeeper 是一个典型的分布式数据一致…...
“深入浅出”系列之C++:(11)推荐一些C++的开源项目
1. SQLiteCpp - 简单易用的Sqlite C封装库 仓库地址:https://github.com/SRombauts/SQLiteCpp 简介:SQLiteCpp是一个对Sqlite数据库进行C封装的开源库,代码行数约2,500行。它提供了简洁易用的接口,使得在C项目中操作Sqlite数据库…...
《重生到现代之从零开始的C++生活》—— 类和对象2
类的默认成员函数 默认成员函数就是用户没有显示实现,编译器会自动生成的成员函数,一个类会默认生成6个成员函数 构造函数 构造函数时特殊的成员函数,构造函数的初始化对象 函数名与类名相同 没有返回值 对象实例化的时候胡自动调用构造…...
“UniApp的音频播放——点击视频进入空白+解决视频播放器切换视频时一直加载的问题”——video.js、video-js.css
今天,又解决了一个单子“UniApp的音频播放——点击视频进入空白解决视频播放器切换视频时一直加载的问题” 一、问题描述 在开发一个基于 video.js 的视频播放器时,用户通过上下滑动切换视频时,视频一直处于加载状态,无法正常播放…...
【Pandas】pandas Series transform
Pandas2.2 Series Function application, GroupBy & window 方法描述Series.apply()用于将一个函数应用到 Series 的每个元素或整个 SeriesSeries.agg()用于对 Series 数据进行聚合操作Series.aggregate()用于对 Series 数据进行聚合操作Series.transform()用于对 Series…...
【博客之星2024年度总评选】年度回望:我的博客之路与星光熠熠
【个人主页】Francek Chen 【人生格言】征途漫漫,惟有奋斗! 【热门专栏】大数据技术基础 | 数据仓库与数据挖掘 | Python机器学习 文章目录 前言一、个人成长与盘点(一)机缘与开端(二)收获与分享 二、年度创…...
飞牛 使用docker部署Watchtower 自动更新 Docker 容器
Watchtower是一款开源的Docker容器管理工具,其主要功能在于自动更新运行中的Docker容器 Watchtower 支持以下功能: 自动拉取镜像并更新容器。 配置邮件通知。 定时执行容器更新任务。 compose搭建Watchtower 1、新建文件夹 先在任意位置创建一个 w…...
【Block总结】TAdaConv时序自适应卷积,轻量高效的时间建模卷积|即插即用
论文解读:Temporally-Adaptive Models for Efficient Video Understanding 论文信息 标题:Temporally-Adaptive Models for Efficient Video Understanding 发表时间:2023年 作者:黄子渊等 论文链接:arXiv 论文 代…...
Spring Boot 项目启动报错 “找不到或无法加载主类” 解决笔记
一、问题描述 在使用 IntelliJ IDEA 开发基于 Spring Boot 框架的 Java 程序时,原本项目能够正常启动。但在后续编写代码并重建项目后,再次尝试运行却出现了 “错误:找不到或无法加载主类 com.example.springboot.SpringbootApplication” 的…...
CSS 网络安全字体
适用于 HTML 和 CSS 的最佳 Web 安全字体 下面列出了适用于 HTM L和 CSS 的最佳 Web 安全字体: Arial (sans-serif)Verdana (sans-serif)Helvetica (sans-serif)Tahoma (sans-serif)Trebuchet MS (sans-serif)Times New Roman (serif)Georgia (serif)Garamond (se…...
Linux高并发服务器开发 第十五天(fork函数)
目录 1.fork 函数 1.1创建子进程 1.2getpid 函数 1.3getppid 函数 1.4getgid函数 1.5循环创建 n 个子进程 1.6fork后父子进程异同 1.6.1读时共享,写时复制 1.6.2fork后父子进程共享 1.6.3gdb调试父子进程 1.fork 函数 pid_t fork(void); 成功:…...
【人工智能】Python中的自动化机器学习(AutoML):如何使用TPOT优化模型选择
《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 随着机器学习在各行业的广泛应用,模型选择和优化成为了数据科学家面临的主要挑战之一。自动化机器学习&am…...
探秘自然地理:从太阳到地球的奇妙之旅与灾害预警
在浩瀚无垠的宇宙中,我们的地球与太阳紧密相连,它们的奥秘和变化,时刻影响着我们的生活。今天,就让我们一同深入探索自然地理的基础知识,揭开太阳与地球的神秘面纱,同时了解那些可能给我们带来巨大影响的自…...
go语言zero框架通过chromedp实现网页在线截图的设计与功能实现
在 GoZero 框架中实现网页在线截图的功能,可以通过集成 chromedp 库来控制 Chrome 浏览器进行截图。chromedp 是一个基于 Chrome DevTools 协议的 Go 包,可以用来在 Go 程序中模拟浏览器操作,如页面截图、DOM 操作、表单提交等。 下面是一个…...
AI发展困境:技术路径与实践约束的博弈
标题:AI发展困境:技术路径与实践约束的博弈 文章信息摘要: AI技术发展路径主要受实践约束驱动,而非纯理论优势。大型AI实验室的成功更依赖优质执行力和资源优势,而非独特技术创新。当前AI发展面临评估体系与实际应用脱…...
stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
DBAPI如何优雅的获取单条数据
API如何优雅的获取单条数据 案例一 对于查询类API,查询的是单条数据,比如根据主键ID查询用户信息,sql如下: select id, name, age from user where id #{id}API默认返回的数据格式是多条的,如下: {&qu…...
Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...
