Spring MVC练习(前后端分离开发实例)

White graces:个人主页

🙉专栏推荐:Java入门知识🙉

🐹今日诗词:二十五弦弹夜月,不胜清怨却飞来🐹


⛳️点赞 ☀️收藏⭐️关注💬卑微小博主🙏

⛳️点赞 ☀️收藏⭐️关注💬卑微小博主🙏


目录

加法计算器

前端页面

约定前后端交互接口

用户登录

约定前后端交互接口

查询用户登录接口

前端页面

同步操作和异步操作

留言板

约定前后端接口

public接口

getList接口

图书管理系统

 应用分层(重点)

美图分享


加法计算器

前端页面

这里的计算器是前后端交互的方式, 单纯的前端也可以实现, 但是就达不到练习的目的了

准备工作: 我们先搭建一个简单的页面, 代码和效果如下: 

<!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>

约定前后端交互接口

  • 简单来说,就是允许客户端给服务器发送哪些HTTP请求,并且每种请求预期获取什么样的HTTP响应, 不能请求A, 你给我返回B, 请求响应必须正确的
  • 现在"前后端分离"模式开发,前端和后端代码通常由不同的团队负责开发.双⽅团队在开发之前,会提前 约定好交互的方式
  • 把约定的内容写在⽂档上,就是"接⼝⽂档",接 ⼝⽂档也可以理解为是应⽤程序的"操作说明书

因此后端代码这样写

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 "计算结果: " + sum;}
}

效果演示

用户登录

约定前后端交互接口

login方法(登录)

首先就是参数效验, 用户名和密码肯定不能为null或者空

参数效验: 1. 可以用if进行多次效验判断(麻烦一点)

2. 使用StringUtils类的hasLength方法进行效验(推荐)

这是登录效验代码

    @RequestMapping("/login")public Boolean login(String username, String password, HttpSession session) {//参数效验(null和空)if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {return false;}//效验账号密码是否正确//这里使用硬编码方式演示, 数据库操作效验密码不方便//假设账号密码是: "zhangsan" "123456"if ("zhangsan".equals(username) && "123456".equals(password)) {//设置Session, 需要创建Sessionsession.setAttribute("username", username);return true;}return false;}

测试一下接口有没问题

查询用户登录接口

​
@RequestMapping("/getUserInfo")public String getUserInfo(HttpSession session) {String username = (String) session.getAttribute("username");return username==null?"":username;}​
这样后端接口就写好了, 接下来搞定前端

前端页面

需要用到Ajax

Ajax,全称是Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)

作用: 

  • 异步:用户在等待服务器响应时可以继续操作界面,提高了用户体验。
  • 无需刷新:局部更新页面,不需要重新加载整个页面。
  • 数据交换格式:虽然名字中包含 XML,但 Ajax 可以使用多种数据格式,包括 JSON、HTML 等,不仅限于 XML

同步操作和异步操作

举例解释二者区别

比如去买饭, 我要卖蛋炒饭和奶茶, 正常情况下是买完蛋炒饭, 等老板做完然后去买奶茶

这种情况就是同步操作

异步操作就是: 我先预购蛋炒饭, 然后我去买奶茶, 等蛋炒饭做好了老板通知去拿就可以了

登录界面

通过Ajax, 登录成功进行页面跳转(重定向), 登录失败就提示错误

<!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.0.min.js"></script><script>function login() {$.ajax({url: "/user/login",  //指定请求的URL地址type: "post",    //指定请求的类型data: {  //指定要发送的数据, 这里的数据是从表单中获取的用户名和密码"userName": $("#userName").val(), // 获取用户名输入框的值"password": $("#password").val() // 获取密码输入框的值},//指定请求成功时的回调函数success: function (result) {// 如果服务器返回的结果为 true,进行页面跳转if (result) {location.href = "index.html"; //重定向到index.html页面} else {alert("密码错误"); //如果结果为 false,弹出密码错误的提示}}});}</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.0.min.js"></script><script>$.ajax({url: "user/getUserInfo",type: "get",success: function (userName) {$("#loginUser").text(userName);}});</script>
</body></html>

留言板

约定前后端接口

设计思路: 

点击提交, 要把数据拼接到留言板下方,

同时需要把数据提交到后端, 交给后端存储, 我们可以通过一个链表存储

并且第一使用留言板, 还要把以前的留言显示在下面(从后端拿到数据并显示在页面上)

因此我们要设计两个接口

  1. message/public接口: 收集前端提交的数据
  2. message/getList接口: 返回数据到前端

因此我们可以这样约定前后端接口

public接口

    @RequestMapping(value = "/public", method = RequestMethod.POST)public Boolean publish(@RequestBody MessageInfo messageInfo) {list.add(messageInfo);System.out.println("日志: 后端存储数据成功");return true;}

getList接口

我们来测试一下接口是否正确

这样后端的代码就没问题了, 接下来我们来写, 前端页面如何通过接口来获取后端数据的代码

前端获取后端数据的代码

分成两步: 

  1.  首先将以前的留言记录显示到下面, 不能每次打开都要重新留言

    由于html代码从上向下执行, 所以这串逻辑代码必须写在script的开头

  2. 创建新的留言, 将新的留言数据交给后端保存, 同时将留言显示到下方 

显示留言记录

保存并显示新的留言

我们来验证一下效果吧

所有代码

留言板所有代码

<!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.0.min.js"></script><script>//$.ajax({url: "message/getList",type: "get",success: function(messages) {if(messages != null) {for(var message of messages) {var tmp = "<div>" + message.from +"对" + message.to + "说:" + message.say +"</div>";$(".container").append(tmp);}}}});//获取留言数据function submit(){let from = $("#from").val();let to = $("#to").val();let say = $("#say").val();//不为空if(from == "" || to == "" || say == ""){return;}//保存并显示新的留言$.ajax({url: "message/public",type: "post",contentType: "application/json",data: JSON.stringify ( {"from": from,"to": to,"say": say} ),success: function(result) {if(result) {//保存成功//将留言信息显示到页面上let message = "<div>" + from +"对" + to + "说:" + say +"</div>";$(".container").append(message);//清空输入信息$("#from").val("");$("#to").val("");$("#say").val("");} else {//保存失败alert("留言失败");}}});}</script>
</body></html>

后端代码

Message对象代码

import lombok.Data;@Data
public class MessageInfo {private String from;private String to;private String say;
}

后端接口代码

import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;import java.util.ArrayList;
import java.util.List;@RestController
@RequestMapping("/message")
public class MessageController {private List<MessageInfo> list = new ArrayList<MessageInfo>();@RequestMapping(value = "/public", method = RequestMethod.POST)public Boolean publish(@RequestBody MessageInfo messageInfo) {list.add(messageInfo);System.out.println("日志: 后端存储数据成功");return true;}@RequestMapping(value = "/getList", method = RequestMethod.GET)public List<MessageInfo> getList() {System.out.println("日志: 前端获取数据成功");return list;}
}

图书管理系统

界面


约定前后端接口

我们先来写登录接口: 判断账号密码是否正确, 首先要有账号密码的属性

import lombok.Data;
@Data
public class UserInfo {private String username;private String password;
}

hasLength()方法: 参数为null或者长度为空, 返回false

equal()方法: 判断二个参数是否相等

@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/login")public String login(String username, String password) {//判断账号或密码是否为空或者长度为0, 为空或0,方法返回falseif (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {return "账号或密码不能为空";}//硬编码方式if (!"zhangsan".equals(username) || !"123456".equals(password)) {return "账号或密码错误";}return "";}
}

 图书列表: getList接口

我们通过链表存储图书, 先创建图书的属性

状态码, 因为图书状态只有可借阅和不可借阅两种情况, 所以可以用数字代替中文, 企业开发用状态码, 显示成借阅和不可借阅是前端的事情

@Data
public class BookInfo {private Integer bookId;  // 图书idprivate String bookName; // 书名private String author;   // 作者private Integer number;  // 数量private BigDecimal price;// 定价private  String publishName;// 出版社private Integer status;  // 状态码, 因为图书状态只有可借阅和不可借阅两种情况, 企业开发用状态码, 变成中文是前端的事情private String statusCN; // (可有可无)状态码对应的中文, 现在我们写项目前后端都是自己, 需要显示一下
}

getList接口, 以及接口测试

package com.white.demo;import org.springframework.util.StringUtils;
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 {private List<BookInfo> list = new ArrayList<BookInfo>();@RequestMapping("/getList")public List<BookInfo> getList() {list = mockData(); //mock测试(模拟测试)return list;}private List<BookInfo> mockData() {  //mock测试(从数据库调用数据进行测试),for (int i = 0; i < 15; i++) {   //由于咱们没数据库, 所以使用mockData方法模拟数据库BookInfo bookInfo = new BookInfo();bookInfo.setBookId(i);bookInfo.setBookName("书名: " + i);bookInfo.setAuthor("作者: " + i);bookInfo.setNumber(3*i+1);bookInfo.setPrice(new BigDecimal(new Random().nextInt(100)));bookInfo.setPublishName("出版社: " + i);bookInfo.setStatus(i%2==0?1:2);bookInfo.setStatusCN(i%2==0?"可借阅":"不可借阅");list.add(bookInfo);}return list;}}

使用postman测试

前端页面

我们要实现从后端拿到数据放到前端, 通过ajax拿到数据后, 如何展示页面呢?

使用HTML的话, 只能一个一个的拼接

拼接方法: 

1. 先只写一对 单引号'' 

例如: 

var tmp = '<tr>123<tr>';

2. 然后把123替换成一对 单引号, 变成这样

tmp = '<tr>''<tr>';     (中间两个单引号)

3.在中间两个单引号之间加上两个+号, 变成这样

tmp = '<tr>'++'<tr>';

4. 再两个 + 号中间填上属性变量即可

tmp = '<tr>'+要替换的变量+'<tr>';

比如这种形式: tmp = '<tr>'+bookId+'<tr>';

拼接结果就是前面两个单引号是一对

根据上述方法, 可以得到以下界面

运行结果

 应用分层(重点)

应用分层是一种设计架构, 将代码分为多个独立的层次, 可以提高代码的可阅读性, 可维护性、可扩展性和灵活性, 并且代码出现问题非常容易找到出错的部分

应用三层一般分为三层: 表现层(Controller), 业务逻辑层(Service), 数据层(Dao), 有时候还会加入实体类层(model, pojo, DTO, VO.....)

表现层(Controller)

作用: 比如用户登录例子, 表现层获取用户的输入(如用户名和密码),将这些数据传递给业务逻辑层

业务逻辑层(Service)

作用: 对用户登录的账户密码进行效验的代码属于业务逻辑层, 效验成功, 调用数据层的数据, 将页面展示出来

数据层(Dao)

作用: 接收业务逻辑层, 存储数据, 或者返回数据给业务逻辑层, 数据层一般不会调用表现层, 业务逻辑层, 

三者调用关系

表现层:创建一个业务逻辑层对象,逻辑层去执行获取的操作
业务逻辑层:创建一个数据层对象,通过数据层对象调用数据库中的数据
数据层:存储数据,提供一个接口方法,让业务逻辑层调用

那我们现在来对代码进行分层吧

首先创建4个包, 用于待会存放分层的代码

我们来对这串代码进行应用分层

表现层:创建一个业务逻辑层对象,逻辑层去执行获取的操作

业务逻辑层:创建一个数据层对象,通过数据层对象调用数据库中的数据

数据层:存储数据,提供一个接口方法,让业务逻辑层调用

美图分享

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/481241.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【ArcGIS Pro实操第11期】经纬度数据转化成平面坐标数据

经纬度数据转化成平面坐标数据 数据准备ArcGIS操作步骤-投影转换为 Sinusoidal1 投影2 计算几何Python 示例 另&#xff1a;Sinusoidal (World) 和 Sinusoidal (Sphere) 的主要区别参考 数据准备 数据投影&#xff1a; 目标投影&#xff1a;与MODIS数据相同&#xff08;Sinu…

丹摩|丹摩智算平台使用教学指南

本指南旨在为新用户提供一个详细的操作步骤和实用的入门指导&#xff0c;帮助大家快速上手丹摩智算平台。 一、平台简介 丹摩智算平台是一款强大的数据分析和计算平台&#xff0c;支持多种编程语言&#xff0c;提供丰富的数据处理和机器学习工具。无论您是数据分析师、开发者…

LLAVA论文简记

LLAVA 1. 研究动机 近年来&#xff0c;随着大语言模型&#xff08;LLM&#xff09;的发展&#xff0c;因此想向着多模态方向扩展。多模态任务&#xff08;例如图像分类、检测、生成等&#xff09;往往需要结合视觉和语言信息&#xff0c;传统的视觉模型在处理这些任务时通常将…

【嵌入式——QT】QT制作安装包

第一步 QT程序写好之后&#xff0c;编译release版本 第二步 拿到release生成的.exe文件 第三步 新建文件夹deploy 第四步 将.exe文件复制到deploy目录下 第五步 在该目录下输入cmd指令&#xff0c;回车 第六步 在打开的命令窗口下输入 windeployqt TegNetCom_1.0.…

YOLOv8模型pytorch格式转为onnx格式

一、YOLOv8的Pytorch网络结构 model DetectionModel((model): Sequential((0): Conv((conv): Conv2d(3, 64, kernel_size(3, 3), stride(2, 2), padding(1, 1))(act): SiLU(inplaceTrue))(1): Conv((conv): Conv2d(64, 128, kernel_size(3, 3), stride(2, 2), padding(1, 1))(a…

分布式储能监控系统为储能电站高效运维与精细化管理赋能

1、引言 随着全球对可持续发展和环境保护意识的增强&#xff0c;能源结构正在经历深刻的转型。传统化石能源因其不可再生性和环境污染问题而逐渐受到限制&#xff0c;而可再生能源如太阳能、风能等因其清洁、可持续的特性而受到广泛关注和推广。这一转型推动了储能技术的快速发…

课题组自主发展了哪些CMAQ模式预报相关的改进技术?

空气污染问题日益受到各级政府以及社会公众的高度重视&#xff0c;从实时的数据监测公布到空气质量数值预报及预报产品的发布&#xff0c;我国在空气质量监测和预报方面取得了一定进展。随着计算机技术的高速发展、空气污染监测手段的提高和人们对大气物理化学过程认识的深入&a…

Avalonia11中读取外部配置文件

背景&#xff1a; 在使用Avalonia开发的过程中需要使用Http请求Api&#xff0c;把Api的BaseUrl appKey等信息写在了代码中&#xff0c;当Api提供发生变化时&#xff0c;需要重新打包客户端程序&#xff0c;于是想着把此部分信息从代码中剥离出来。 需求&#xff1a; 请求服务…

解析客服知识库搭建的五个必要性

在当今竞争激烈的商业环境中&#xff0c;客服知识库的搭建已成为企业提升服务质量、优化客户体验的重要手段。一个完善的客服知识库不仅能帮助企业高效管理客户服务流程&#xff0c;还能显著提升客户满意度和忠诚度。以下是搭建客服知识库的五个必要性&#xff1a; 1. 提升服务…

Springboot 修改post请求接口入参或重新赋值

前言 很久之前写过一篇就是自动填充接口参数的&#xff0c;利用的 HandlerMethodArgumentResolver 自定义注解 Springboot Controller接口默认自动填充 业务实体参数值_springboot设置入参默认值-CSDN博客 现在这一篇也差不多&#xff0c;达到的目的就是重新去给post请求的参数…

如何创建一个Next.js项目(超简单)

1、安装Node.js&#xff08;官网Node.js下载也行&#xff0c;但Windows更加推荐nvm工具&#xff09; 2、运行node -v和npm -v两条命令&#xff08;检验是否下载成功Node.js&#xff09; 3、npx create-next-applatest&#xff08;使用 启动一个新的 Next.js 应用 create-next…

2024农历年余下的数模比赛名单已出炉!

数学建模比赛季又来了&#xff01;作为一名资深的数学建模辅导老师&#xff0c;我想对你们说&#xff1a;这不仅是挑战智商的时候&#xff0c;也是展现团队合作力、数据分析能力和逻辑思维的最佳舞台&#xff01;&#x1f4a1; 如果你是建模新手&#xff0c;或者想让自己的比赛…

Spring Cloud 程序读取 nacos 中的配置信息

本文主要介绍如何用 Spring Cloud 程序读取 nacos 中的配置信息 文章目录 一、启动nacos二、在nacos中添加配置项二、order-service项目读取配置项1. 项目的pom.xml 文件2. bootstrap.yml 配置文件&#xff0c;配置nacos3. Controller方法中读取nacos配置4. URL调用接口&#x…

在鸿蒙应用中 Debug 对开发者的帮助

文章目录 摘要引言Debug 的意义与挑战案例&#xff1a;页面渲染性能优化中的 Bug 排查Debug 过程详解问题定位问题解决优化布局与渲染逻辑 代码详细讲解示例代码详细讲解1. 导入必要模块2. 数据生成3. 使用虚拟列表组件items 属性itemHeight 属性renderItem 属性 4. 返回完整组…

SpringBoot小知识(3):热部署知识

一、热部署 热部署是一个非常消耗内存的机制&#xff0c;在实际大型项目开发中几乎用不到&#xff0c;只有小型项目或者分模块或者不停机更新的时候才会用到&#xff0c;仁者见仁智者见智。 1.1 什么是热部署&#xff1f; 热部署是指在不停止应用程序或服务器的情况下&#xf…

【ArcGIS Pro】实现一下完美的坐标点标注

在CAD里利用湘源可以很快点出一个完美的坐标点标注。 但是在ArcGIS Pro中要实现这个效果却并不容易。 虽然有点标题党&#xff0c;这里就尽量在ArcGIS Pro中实现一下。 01 标注实现方法 首先是准备工作&#xff0c;准备一个点要素图层&#xff0c;包含xy坐标字段。 在地图框…

.net XSSFWorkbook 读取/写入 指定单元格的内容

方法如下&#xff1a; using NPOI.SS.Formula.Functions;using NPOI.SS.UserModel;using OfficeOpenXml.FormulaParsing.Excel.Functions.DateTime;using OfficeOpenXml.FormulaParsing.Excel.Functions.Numeric;/// <summary>/// 读取Excel指定单元格内容/// </summa…

【Redis】Redis介绍

目录 1.Redis是什么? 2. Redis特性 2.1 速度快 2.2 基于键值对的数据结构服务器 2.3 丰富的功能 2.4 简单稳定 2.5 客户端语言多 2.6 持久化 2.7 主从复制 2.8 高可用和分布式 3. Redis使用场景 3.1 缓存(Cache) 3.2 排行榜系统 3.3 计数器应用 3.4 社交网络 …

使用postcss动态设置fontsize,刷新时出现极小页面的问题

最近做的一个项目&#xff0c;使用了postcss来动态调整fontsize的大小&#xff0c;使用rem来让页面比例保持一致&#xff0c;配置如下&#xff1a; // vite.config.js css: {postcss: {plugins: [postcssPxtorem({rootValue: 192, // UI设计稿的宽度/10unitPrecision: 3, // 转…

RabbitMQ在手动消费的模式下设置失败重新投递策略

最近在写RabbitMQ的消费者&#xff0c;因为业务需求&#xff0c;希望失败后重试一定次数&#xff0c;超过之后就不处理了&#xff0c;或者放入死信队列。我这里就达到重试次数后就不处理了。本来以为很简单的&#xff0c;问了kimi&#xff0c;按它的方法配置之后&#xff0c;发…