[Spring] Spring Web MVC案例实战

🌸个人主页:https://blog.csdn.net/2301_80050796?spm=1000.2115.3001.5343
🏵️热门专栏:
🧊 Java基本语法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12615970.html?spm=1001.2014.3001.5482
🍕 Collection与数据结构 (92平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm=1001.2014.3001.5482
🧀Java EE(96平均质量分) https://blog.csdn.net/2301_80050796/category_12643370.html?spm=1001.2014.3001.5482
🍭MySql数据库(93平均质量分)https://blog.csdn.net/2301_80050796/category_12629890.html?spm=1001.2014.3001.5482
🍬算法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12676091.html?spm=1001.2014.3001.5482
🍃 Spring(97平均质量分)https://blog.csdn.net/2301_80050796/category_12724152.html?spm=1001.2014.3001.5482
感谢点赞与关注~~~
在这里插入图片描述

目录

  • 1. 加法计算器
    • 1.1 约定前后端交互的接口(接口文档)
    • 1.2 前端代码
    • 1.3 后端代码
  • 2. 用户登录
    • 2.1 接口文档
    • 2.2 前端代码
      • 2.2.1 登录页面
      • 2.2.2 首页
    • 2.3 后端代码
      • 2.3.1 登录页面
      • 2.3.2 主页
  • 3. 留言板
    • 3.1 接口文档
    • 3.2 前端代码
    • 3.3 后端代码
      • 3.3.1 lombok介绍
  • 4. 图书管理系统
    • 4.1 接口文档
    • 4.2 前端代码
    • 4.3 后端代码
  • 5. 应用分层
    • 4.1 介绍
    • 4.2 具体在项目中的体现

1. 加法计算器

需求:输入两个整数,点击"点击相加"按钮,显示计算结果.

1.1 约定前后端交互的接口(接口文档)

这是Web开发中的关键一环.接口又叫API,我们一般讲到的API或者接口,指的都是同一个东西.如今我们的开发一般采用前后端分离的方式,所以我们在开发之前,前端开发人员和后端开发人员会约定好前后端交互的方式.我们一般会把约定的内容写在文档上,就是"接口文档".接口文档可以理解为是应用程序中的"操作说明书".
在项目开发之前.我们需要先更具需求拟写接口文档,前后端必须都准寻接口文档中的标准.**接口文档通常由服务提供方来写,有服务使用方确认,也就是客户端.**关于接口文档怎么写,每个公司有不同的标准,一般是需求分析和接口定义(接口名称,URL),传递参数,返回参数下面我们来拟写这个案例的简单接口文档:

需求分析: 输入两个整数,点击"点击相加"按钮,显示计算结果.
接口定义:

请求路径:calc/sum,
请求方式:GET/POST,
接口描述:计算两个整数相加

请求参数:

参数名类型是否必须备注
num1Integer参与计算的第⼀个数
num2Integer参与计算的第⼆个数
响应数据:
Content-Type: text/html
响应内容:计算机计算的结果

1.2 前端代码

首先,我们需要准备前端的代码.把前端的代码calc.html放在项目的Static目录中.
在这里插入图片描述

<!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">
<!--上面的action部分就表示的是与后端交互的接口,可以从这个接口中给前端返回数据--><h1>计算器</h1>数字1:<input name="num1" type="text"><br>数字2:<input name="num2" type="text"><br><input type="submit" value=" 点击相加 "></form>
</body></html>

1.3 后端代码

package com.example.demo;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/calc")
@RestController
public class Calc {@RequestMapping("/sum")public String sum(Integer num1,Integer num2){Integer sum = num1 + num2;return "<h1>加和结果</h1>" + sum;}
}

首先使用查询字符串来给参数传递值来测试后端代码的正确性.后端代码的逻辑没有问题.
在这里插入图片描述
之后我们连带前端代码一起运行起来.
在这里插入图片描述
在这里插入图片描述

如果前后端交互的时候出现了一些问题的时候,我们一般按照下面这样的步骤来解决:

  1. 首先清理前端(Ctrl+f5刷新页面)缓存,和后端(pom:clean)缓存.
  2. 首先看错误日志
  3. 确认后端接口是否有问题(可以通过浏览器或者Postman访问)
  4. 前端请求时,Fiddler抓包或者Debug,观察接参数或者URL是否有问题.

2. 用户登录

需求:用户输入账号和密码,后端进行校验密码是否正确.

  1. 如果正确,跳转到首页,首页显示当前登录用户的用户名
  2. 如果错误,前端进行用户告知.
  3. 后续在访问首页,可以获取到登录用户信息.

2.1 接口文档

需求分析:

用户输入账号和密码,后端进行校验密码是否正确.

  1. 如果正确,跳转到首页,首页显示当前登录用户的用户名
  2. 如果错误,前端进行用户告知.
  3. 后续在访问首页,可以获取到登录用户信息.
  • 登录页面

    接口定义:

    请求路径: /user/login
    请求方式: POST
    接口描述: 校验账号和密码的正确性.
    

    请求参数:

    参数名类型是否必须备注
    userNameString校验的账号
    passwordString校验的密码

    响应数据:

    Content-Type : text/html
    响应内容: 
    账号密码正确:true
    账号密码错误:false
    
  • 主页
    接口定义:

    请求路径: /user/getLoginuser
    请求方式: GET
    接口描述: 显示当前登录用户的主页,主页上显示用户名.
    

    请求参数:

    响应数据:

    Content-Type:text/html
    响应内容: 登录的用户名.
    

2.2 前端代码

对于前端而言,点击登录按钮的时候,需要把用户传递的信息传递到后端进行校验,后端校验成功之后,则跳转到首页:index.html,后端校验失败之后,直接弹窗.

2.2.1 登录页面

<!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="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script><script>function login() {//使用ajax进行前后端交互$.ajax({//小括号中是一个对象,对象用大括号括起来type:"post",url:"/user/login",data:{"username":$("#userName").val(),"password":$("#password").val()//通过Id获取值,给后端传递参数},success: function (result) {//参数名任意,用于接收后端返回的参数if (result){location.href = "/index.html"//跳转页面}else {alert("账号或密码有误");//弹窗}}});}</script>
</body></html>
  • 这里我们使用ajax来进行信息传递,不用form表单的原因,是为了在输入错误的时候,不让页面发生跳转,如果使用form表单的话,页面一定会发生跳转.再者,因为ajax是异步调用的,在ajax的头部把信息先留下,之后再说对信息如何处理以及如何做.

何为异步?比如我们去街道处办事,我们需要先提交我们的资料,但是给我办事的那个人不在,同步操作就是一直等,等到那个人来,异步就是先把资料留下,先回家,等事情办好之后,给你打电话.

  • success: function (result)其中的success表示的是接口返回结果的成功和失败,而不是业务结果返回true或者是false.

比如我们去银行办理业务,有三种可能:

  1. 银行没开门
  2. 忘记带身份证了,业务办理失败
  3. 证件携带齐全,业务办理成功
    第一种就是接口返回了错误信息,第二种就是业务逻辑返回false,第三种就是业务逻辑返回true.
  • 页面跳转的三种方式:
  1. window.location.href=index.html
  2. window.location.assign(“index.html”)
  3. window.location.replace(“index.html”)
    我们一般把window省略.1,2是等价的,在进入新的页面之后,都可以回退回上一个页面,而3无法回退到上一个页面.

2.2.2 首页

<!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="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script><script>$.ajax({type : "get",url : "/user/getLoginUser",success:function (result) {$("#loginUser").text(result);//给loginUser参数赋值为后端返回的result值}})</script>
</body></html>

2.3 后端代码

2.3.1 登录页面

package com.example.demo;import jakarta.servlet.http.HttpSession;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/user")
@RestController
public class Login {@RequestMapping("/login")public Boolean login(String userName, String password, HttpSession session){//确保输入的密码和账号都不为空//也为了保证前端传递信息成功,不会传递一个null过来if (!StringUtils.hasLength(userName) ||!StringUtils.hasLength(password)){return false;}if (!"zhangsan".equals(userName) ||! "123456".equals(password)){return false;}//密码和账号都正确,设置sessionsession.setAttribute("userName",userName);return true;}
}

其中StringUtils.hasLength()方法是Spring中提供的一个工具方法,判断字符串是否有值.字符串为null或者是""时,返回false,其他返回true.

public static boolean hasLength(@Nullable String str) {return str != null && !str.isEmpty();
}

2.3.2 主页

package com.example.demo;import jakarta.servlet.http.HttpSession;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/user")
@RestController
public class GetUserLogin {@RequestMapping("/getUserLogin")public String getUserLogin(HttpSession httpSession){//从session中获取用户名String userName = (String) httpSession.getAttribute("userName");if (StringUtils.hasLength(userName)){//确保userName有值return userName;}return null;}
}

运行代码:
在这里插入图片描述
登录成功:
在这里插入图片描述
登录失败:
在这里插入图片描述

3. 留言板

3.1 接口文档

需求分析:

  1. 提交留言:用户输⼊留言信息之后,后端需要把留言信息保存起来
  2. 展示留言:页面展示时,需要从后端获取到所有的留言信息

接口定义:

  1. 获取全部留言
    全部留言信息,我们用List来表示,可以用JSON来描述这个List数据.
    请求:
    GET /message/getList
    
    响应:JSON格式
    [{"from": "黑猫","to": "白猫","message": "喵"
    },{"from": "黑狗","to": "白狗","message": "汪"},//...
    ]
    
    浏览器给服务器发送⼀个GET /message/getList 这样的请求,就能返回当前⼀共有哪些留言记录.结果以json的格式返回过来.
  2. 发表新留言
    请求:body也为JSON格式.
    POST /message/publish
    {"from": "黑猫","to": "白猫","message": "喵"
    }
    响应:JSON格式.
    {ok: 1
    }
    
    我们期望浏览器给服务器发送⼀个POST /message/publish 这样的请求,就能把当前的留言提交给服务器.

3.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="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script><script>load();//每次在重新加载页面之后,都要从后端的List中调动数据,保证上次添加的数据不会丢失function load(){$.ajax({type: "get",url:"message/getList",success:function (result){for (var message of result){var divE = "<div>"+message.from +"对" + message.to + "说:" + message.say+"</div>";$(".container").append(divE);}}});}function submit(){//1. 获取留言的内容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",//传递的值是json类型,data就是在向后端传递数据data:JSON.stringify({from : from,to : to,say : say//从前端参数的ID中获取对应的值传递给后端}),//后端返回结果success:function (result) {if (result){//2. 构造节点var divE = "<div>"+from +"对" + to + "说:" + say+"</div>";//3. 把节点添加到页面上$(".container").append(divE);//4. 清空输入框的值$('#from').val("");$('#to').val("");$('#say').val("");}else{alert("提交留言失败")}}});}</script>
</body></html>

3.3 后端代码

package com.example.demo;import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.ArrayList;
import java.util.List;@RequestMapping("/message")
@RestController
public class MessageWall {public List<MessageInfo> messageInfoList = new ArrayList<>();@RequestMapping("/publish")public Boolean messageController(@RequestBody MessageInfo messageInfo){System.out.println(messageInfo);//打印日志if (StringUtils.hasLength(messageInfo.from) &&StringUtils.hasLength(messageInfo.to) &&StringUtils.hasLength(messageInfo.say)){messageInfoList.add(messageInfo);return true;//都有长度,添加成功,返回true}//添加失败,返回falsereturn false;}@RequestMapping("/getList")public List<MessageInfo> getList(){return messageInfoList;}
}
package com.example.demo;import lombok.Data;@Data
public class MessageInfo {public String from;public String to;public String say;
}

3.3.1 lombok介绍

Lombok是⼀个Java工具库,通过添加注解的方式,简化Java的开发.

  1. 引入依赖
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional>
</dependency>
  1. 使用
    lombok通过使用一些注释的方式,可以帮我们消除一些冗长的代码,让代码看起来更简洁.
    比如我们之前的Person对象就可以简化为:
package com.example.demo;import lombok.Data;@Data
public class Person {public String name;public int age;public String sex;
}

其中,@Data注解会帮助我们自动⼀些方法,包含getter/setter,equals,toString等.
3. 更多使用方法
@Data生成的方法太多,lombok页为我们提供了一些颗粒度更细的注解.

注解作用
@Getter自动添加getter方法
@Setter自动添加setter方法
@ToString自动添加toString方法
@EqualsAndHashCode自动添加equals和hashCode方法
@NoArgsConstructor自动添加无参构造方法
@AllArgsConstructor自动添加全属性构造方法,顺序按照属性的定义顺序
@NonNull属性不能为null
@RequiredArgsConstructor自动添加必需属性的构造方法,final+@NonNull的属性为必需

其中@Data = @Getter+@Setter+@ToString+@NoArgsConstructor+@RequiredArgsConstructor

下面来测试运行:
在这里插入图片描述

4. 图书管理系统

4.1 接口文档

  1. 需求;
    登录:用户输入账号和密码完成登录功能.
    列表展示:展示图书
  2. 接口定义
    登录接口
    [URL]
    POST /user/login
    [请求参数]
    name=admin&password=admin
    [响应]
    true //账号密码验证成功
    false//账号密码验证失败
    
    1. 图书列表展示
    [URL]
    POST /book/getList
    [请求参数]
    ⽆
    [响应]
    返回图书列表
    [{"id": 1,"bookName": "活着","author": "余华","count": 270,"price": 20,"publish": "北京⽂艺出版社","status": 1,"statusCN": "可借阅"},...
    
    字段说明:
    id图书ID
    bookName图书名称
    author作者count 数量
    price定价
    publish图书出版社
    status图书状态 1-可借阅,2-不可借阅
    statusCN图书状态中文含义

4.2 前端代码

  • 登录页面
<!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>
  • 图书列表
<!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="op">';finalHtml += '<a href="book_update.html?bookId=' + book.id + '">修改</a>';finalHtml += '<a href="javascript:void(0)"οnclick="deleteBook(' + book.id + ')">删除</a>';finalHtml += '</div></td>';finalHtml += "</tr>";}$("tbody").html(finalHtml);}}});}//翻页信息$("#pageContainer").jqPaginator({totalCounts: 100, //总记录数pageSize: 10,    //每页的个数visiblePages: 5, //可视页数currentPage: 1,  //当前页码first: '<li class="page-item"><a class="page-link">首页</a></li>',prev: '<li class="page-item"><a class="page-link" href="javascript:void(0);">上一页<\/a><\/li>',next: '<li class="page-item"><a class="page-link" href="javascript:void(0);">下一页<\/a><\/li>',last: '<li class="page-item"><a class="page-link" href="javascript:void(0);">最后一页<\/a><\/li>',page: '<li class="page-item"><a class="page-link" href="javascript:void(0);">{{page}}<\/a><\/li>',//页面初始化和页码点击时都会执行onPageChange: function (page, type) {console.log("第"+page+"页, 类型:"+type);}});function deleteBook(id) {var isDelete = confirm("确认删除?");if (isDelete) {//删除图书alert("删除成功");}}function batchDelete() {var isDelete = confirm("确认批量删除?");if (isDelete) {//获取复选框的idvar ids = [];$("input:checkbox[name='selectBook']:checked").each(function () {ids.push($(this).val());});console.log(ids);alert("批量删除成功");}}</script></div>
</body></html>

4.3 后端代码

  • 登录页面
package com.jrj.library;import jakarta.servlet.http.HttpSession;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/user")
@RestController
public class Login {@RequestMapping("/login")public Boolean login(String name, String password, HttpSession session){if (!StringUtils.hasLength(name) || !StringUtils.hasLength(password)){return false;}if ("zhangsan".equals(name) && "123456".equals(password)){session.setAttribute("userName",name);return true;}return false;}
}
  • 图书列表
    创建图书的属性
    package com.jrj.library;import lombok.Data;@Data
    public class BookInfo {//构造一本书所有的属性public Integer id;public String bookName;public String author;public Integer count;public Integer price;public String publish;public Integer status;//1-可借阅,2-不可借阅public String statusCN;
    }
    返回图书列表:
    package com.jrj.library;import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;import java.util.ArrayList;
    import java.util.List;
    import java.util.Random;@RequestMapping("/book")
    @RestController
    public class BookController {@RequestMapping("/getList")public List<BookInfo> getList(){List<BookInfo> list = mockData();for (BookInfo bookInfo:list){if (bookInfo.status == 1){bookInfo.setStatusCN("可借阅");}else{bookInfo.setStatusCN("不可借阅");}}return list;}//模拟数据private List<BookInfo> mockData(){List<BookInfo> list2 = new ArrayList<>();for (int i = 0; i < 5; i++) {BookInfo bookInfo = new BookInfo();bookInfo.setId(i);bookInfo.setBookName("Java编程思想"+i);bookInfo.setCount(1);bookInfo.setPublish("机械工业出版社");bookInfo.setPrice(new Random().nextInt(100));bookInfo.setAuthor("高斯林");bookInfo.setStatus(1);list2.add(bookInfo);}return list2;}
    }

测试运行:
在这里插入图片描述
登录页面正常
在这里插入图片描述
可正常登录,图书列表页面展示正确.

5. 应用分层

通过上面的几个案例,我们看到我们的代码平铺在我们的项目中,显得非常杂乱.所以我们要使用应用分层.
在这里插入图片描述

4.1 介绍

常见的应用分层结构如下:
在这里插入图片描述
我们之前提到的"MVC",就是把整体的系统分成了Model(模型),View(视图)和Controller(控制器)三个层次.现在我们主流开发的方式是"前后端分离"的方式,后端开发不再需要关心前端的实现,所以对java后端开发者,又有了一种新的分层架构:把整体架构分为表现层、业务逻辑层和数据层.这种分层方式也称之为"三层架构".

  1. 表现层:就是展示数据结果和接受用户指令(接收参数和返回结果)的,是最靠近用户的⼀层;
  2. 业务逻辑层:负责处理业务逻辑,里面有复杂业务的具体实现(拿到参数之后进行方法的具体实现);
  3. 数据层:负责存储和管理与应用程序相关的数据(比如数据库交互)

4.2 具体在项目中的体现

在我们创建Spring项目中,具体对分层的实现就是创建一个一个不同的目录,把代码分层管理起来.其中不同层面的目录一般用以下的命名方式:
• Controller:控制层。接收前端发送的请求,对请求进行处理,并响应数据。
• Service:业务逻辑层。处理具体的业务逻辑。
• Dao:数据访问层,也称为持久层。负责数据访问操作,包括数据的增、删、改、查.
• Model: 用于存储对实物属性的描述
下面我们对之前的图书管理的代码进行拆分重构:

  • 表现层
package com.jrj.library.controller;import com.jrj.library.service.LoginService;
import jakarta.servlet.http.HttpSession;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RequestMapping("/user")
@RestController
public class LoginController {@RequestMapping("/login")public boolean login(String name, String password, HttpSession session){LoginService loginService = new LoginService();return loginService.login(name,password,session);}
}
package com.jrj.library.controller;import com.jrj.library.BookInfo;
import com.jrj.library.service.BookService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.ArrayList;
import java.util.List;@RequestMapping("/book")
@RestController
public class BookController {BookService bookService = new BookService();@RequestMapping("/getList")public List<BookInfo> getList(){List<BookInfo> bookInfos = new ArrayList<>();bookInfos = bookService.getList();return bookInfos;}
}
  • 业务逻辑层
package com.jrj.library.service;import com.jrj.library.BookInfo;
import com.jrj.library.dao.Data;import java.util.List;public class BookService {public List<BookInfo> getList(){Data data = new Data();List<BookInfo> list = data.mockData();for (BookInfo bookInfo:list){if (bookInfo.status == 1){bookInfo.setStatusCN("可借阅");}else{bookInfo.setStatusCN("不可借阅");}}return list;}
}
package com.jrj.library.service;import jakarta.servlet.http.HttpSession;
import org.springframework.util.StringUtils;public class LoginService {public Boolean login(String name, String password, HttpSession session){if (!StringUtils.hasLength(name) || !StringUtils.hasLength(password)){return false;}if ("zhangsan".equals(name) && "123456".equals(password)){session.setAttribute("userName",name);return true;}return false;}
}
  • 数据访问层
import java.util.Random;public class Data {//向业务逻辑端提供数据public List<BookInfo> mockData(){List<BookInfo> list2 = new ArrayList<>();for (int i = 0; i < 5; i++) {BookInfo bookInfo = new BookInfo();bookInfo.setId(i);bookInfo.setBookName("Java编程思想"+i);bookInfo.setCount(1);bookInfo.setPublish("机械工业出版社");bookInfo.setPrice(new Random().nextInt(100));bookInfo.setAuthor("高斯林");bookInfo.setStatus(1);list2.add(bookInfo);}return list2;}
}
  • 实物描述
package com.jrj.library.model;import lombok.Data;@Data
public class BookInfo {//构造一本书所有的属性public Integer id;public String bookName;public String author;public Integer count;public Integer price;public String publish;public Integer status;//1-可借阅,2-不可借阅public String statusCN;
}

上面的"三层架构",遵循了一种软件设计的原则,叫做"高内聚,低耦合".
高内聚指的是⼀个模块中各个元素之间的联系比较紧密.如果各个元素(语句、程序段)之间的联系程度越高,则内聚性越高,即"高内聚".
低耦合指的是软件中各个层、模块之间的依赖关联程序越低越好。修改⼀处代码,其他模块的代码改动越少越好.
在这里插入图片描述

前面提到的MVC架构模式和三层架构模式有什么区别?
MVC架构模式由三部分组成,分别是:模型(Model),视图(View)和控制器(Controller).
三层架构将业务应用划分为:表现层,业务逻辑层,数据访问层.
MVC模式强调数据和视图分离,将数据展示和数据处理分开,通过控制器对两者进行组合
三层架构强调不同维度数据处理的高内聚和低耦合,将交互界面,业务处理和数据库操作的逻辑分开.
在这里插入图片描述

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

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

相关文章

AV1技术学习:Translational Motion Compensation

编码块根据运动矢量在参考帧中找到相应的预测块&#xff0c;如下图所示&#xff0c;当前块的左上角的位置为(x0, y0)&#xff0c;在参考帧中找到同样位置(x0, y0)的块&#xff0c;根据运动矢量移动到目标参考块&#xff08;左上角位置为&#xff1a;(x1, y1)&#xff09;。 AV1…

前端a-tree遇到的问题

在使用a-tree时候&#xff0c;给虚拟滚动的高度&#xff0c;然后展开a-tree滑动一段距离 比如这样 随后你切换页面&#xff0c;在返回这个页面的时候 就会出现这样的bug 解决方法&#xff1a; onBeforeRouteLeave((to, from, next) > {// 可以在路由参数变化时执行的逻辑ke…

白山云荣获信通院“算网安全行业应用优秀案例”奖

日前&#xff0c;在由中国通信标准化协会算网融合产业及标准推进委员会与信通院共同组织召开的“2024年算网融合产业发展大会”上&#xff0c;白山云凭借创新的SD-WAN算网融合方案&#xff0c;荣获“算网安全行业应用优秀案例”奖。 算网融合是多元异构、海量泛在的算力设施&am…

path模块和HTTP协议

一。path模块常用API ./相对路径&#xff0c;/绝对路径 二&#xff0c;HTTP协议 1.请求报文 1.请求行 URL的组成 2.请求头 3.请求体 可以是空&#xff1a;GET请求 可以是字符串&#xff0c;还可以是json&#xff1a;POST请求 2.响应报文 1.响应行 HTTP / 1.1 200 OK H…

VsCode 与远程服务器 ssh免密登录

首先配置信息 加入下列信息 Host qb-zn HostName 8.1xxx.2xx.3xx User root ForwardAgent yes Port 22 IdentityFile ~/.ssh/id_rsa 找到自己的公钥&#xff0c;不带pub是私钥&#xff0c;打死都不能给别人。复制公钥 拿到公钥后&#xff0c;来到远程服务器 vim ~/.ss…

Leetcode—3011. 判断一个数组是否可以变为有序【中等】(__builtin_popcount()、ranges::is_sorted())

2024每日刷题&#xff08;144&#xff09; Leetcode—3011. 判断一个数组是否可以变为有序 O(n)复杂度实现代码 class Solution { public:bool canSortArray(vector<int>& nums) {// 二进制数位下1数目相同的元素就不进行组内排序// 只进行分组// 当前组的值若小于…

人工智能算法工程师(中级)课程12-PyTorch神经网络之LSTM和GRU网络与代码详解1

大家好,我是微学AI,今天给大家介绍一下人工智能算法工程师(中级)课程12-PyTorch神经网络之LSTM和GRU网络与代码详解。在深度学习领域,循环神经网络(RNN)因其处理序列数据的能力而备受关注。然而,传统的RNN存在梯度消失和梯度爆炸的问题,这使得它在长序列任务中的表现不尽…

MySQL--C_C++语言连接访问

Connector/C的使用 首先需要在mysql官网下载C接口库 解压指令 tar -zxvf 压缩包名 下载并解压好后 但是还有比这更优的做法。 这样子手动安装不仅麻烦&#xff0c;还可能存在兼容性的问题。 其实在我们使用yum安装mysql时&#xff0c;大概率会自动帮我们把其他的环境都安装…

【Datawhale AI夏令营】电力需求预测挑战赛 Task01

整个学习活动&#xff0c;将带你从 跑通最简的Baseline&#xff0c;到了解竞赛通用流程、深入各个竞赛环节&#xff0c;精读Baseline与进阶实践 文章目录 一、赛题背景二、赛题任务三、实践步骤学习规划分析思路常见时序场景 task01codecode 解读 一、赛题背景 随着全球经济的…

CSA笔记1-基础知识和目录管理命令

[litonglocalhost ~]$ 是终端提示符&#xff0c;类似于Windows下的cmd的命令行 litong 当前系统登录的用户名 分隔符 localhost 当前机器名称&#xff0c;本地主机 ~ 当前用户的家目录 $ 表示当前用户为普通用户若为#则表示当前用户为超级管理员 su root 切换root权限…

昇思25天学习打卡营第12天|munger85

基于MindSpore通过GPT实现情感分类 这个实现情感分类意思就是通过一些电影的数据最后知道他对于这个电影的评价&#xff0c;最后知道他对于这个电影的评价到底是好还是不好&#xff0c;零就是不好&#xff0c;一就是好。首先我们肯定是按安装这些依赖包了为了今天这个模型我们…

【Apache Doris】周FAQ集锦:第 14 期

【Apache Doris】周FAQ集锦&#xff1a;第 14 期 SQL问题数据操作问题运维常见问题其它问题关于社区 欢迎查阅本周的 Apache Doris 社区 FAQ 栏目&#xff01; 在这个栏目中&#xff0c;每周将筛选社区反馈的热门问题和话题&#xff0c;重点回答并进行深入探讨。旨在为广大用户…

深度加速器 为游戏而生

使用深度加速器的基本步骤如下 首先&#xff0c;访问深度加速器的官方网站或授权下载渠道&#xff0c;下载最新版本的深度加速器客户端。 下载完成后&#xff0c;电脑版直接双击打开免安装&#xff0c;将深度加速器安装到您的计算机或移动设备上。 注册与登录&#xff1a; 打…

如何构建全生命周期的安全体系架构来确保容器的安全?

容器技术在云原生应用和微服务架构中得到了广泛应用&#xff0c;其轻量、灵活和高效的特点使其成为现代IT环境中的重要工具。然而&#xff0c;尽管容器带来了许多优势&#xff0c;但其安全性问题也不容忽视。接下来跟随博主一起探索如何构建全生命周期的安全体系架构以确保容器…

Vue3 子组件像父组件传递数据 自定义事件 defineEmits

介绍 很多情况下子组件都需要像父组件去传递一些数据&#xff0c;Vue3和Vue2传递值的写法不太一样。 例子 很常见的一个案例&#xff0c;弹出一个商品对话框&#xff0c;用户选择商品后把商品信息返回给父组件&#xff0c;使用自定义事件去做。 子组件 选择商品对话框 &…

数据库操作太复杂?Python Shelve模块让你轻松存储,一键搞定!

目录 1、基本操作入门 &#x1f4da; 1.1 安装Shelve模块 1.2 创建与打开Shelve文件 2、存储与读取数据 &#x1f510; 2.1 写入键值对 2.2 读取存储的数据 3、高级功能探索 &#x1f9ed; 3.1 使用Shelve迭代键和值 3.2 键的管理&#xff1a;添加、删除与更新 4、异…

详解曼达拉升级:如何用网络拓扑结构扩容BSV区块链

​​发表时间&#xff1a;2024年5月24日 BSV曼达拉升级是对BSV基础设施的战略性重塑&#xff0c;意在显著增强其性能&#xff0c;运行效率和可扩容。该概念于2018年提出&#xff0c;其战略落地将使BSV区块链顺利过渡&#xff0c;从现有的基于单一集成功能组件的网络拓扑结构&am…

MySQL面试篇章——MySQL基础复习

文章目录 MySQL基本介绍MySQL数据类型数值类型字符串类型日期和时间类型ENUM和SET MySQL运算符算数运算符逻辑运算符比较运算符 MySQL常用函数字符串函数数值函数时间和日期函数聚合函数 MySQL完整性约束范式第一范式&#xff08;1NF&#xff09;第二范式&#xff08;2NF&#…

有关电力电子技术的一些相关仿真和分析:⑤交-直-交全桥逆变+全波整流结构电路(MATLAB/Siumlink仿真)

全桥逆变+全波整流结构 参数:Vin=500V, Vo=200V, T=2:1:1, RL=10Ω, fs=100kHz, L=1mH, C=100uF (1)给定输入电压,输出电压和主电路参数,仿真研究电路工作原理,分析工作时序; (2)调节负载电阻,实现电流连续和断续,并仿真验证; (3)调节占空比,分析占空比与电…

设计模式总结(设计模式的原则及分类)

1.什么是设计模式&#xff1f; 设计模式(Design pattern)代表了最佳的实践&#xff0c;通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结…