[Spring] Spring Web MVC案例实战
LileSily 2024-07-18 15:03:01 阅读 50
🌸个人主页: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),传递参数,返回参数下面我们来拟写这个案例的简单接口文档:
需求分析: 输入两个整数,点击"点击相加"按钮,显示计算结果.
接口定义:
<code>请求路径:calc/sum,
请求方式:GET/POST,
接口描述:计算两个整数相加
请求参数:
参数名 类型 是否必须 备注 num1 Integer 是 参与计算的第⼀个数 num2 Integer 是 参与计算的第⼆个数 响应数据:
Content-Type: text/html
响应内容:计算机计算的结果
1.2 前端代码
首先,我们需要准备前端的代码.把前端的代码calc.html放在项目的Static目录中.
<code><!DOCTYPE html>
<html lang="en">code>
<head>
<meta charset="UTF-8">code>
<meta name="viewport" content="width=device-width, initial-scale=1.0">code>
<title>Document</title>
</head>
<body>
<form action="calc/sum" method="post">code>
<!--上面的action部分就表示的是与后端交互的接口,可以从这个接口中给前端返回数据-->
<h1>计算器</h1>
数字1:<input name="num1" type="text"><br>code>
数字2:<input name="num2" type="text"><br>code>
<input type="submit" value=" 点击相加 ">code>
</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;
}
}
首先使用查询字符串来给参数传递值来测试后端代码的正确性.后端代码的逻辑没有问题.
之后我们连带前端代码一起运行起来.
如果前后端交互的时候出现了一些问题的时候,我们一般按照下面这样的步骤来解决:
首先清理前端(Ctrl+f5刷新页面)缓存,和后端(pom:clean)缓存.首先看错误日志确认后端接口是否有问题(可以通过浏览器或者Postman访问)前端请求时,Fiddler抓包或者Debug,观察接参数或者URL是否有问题.
2. 用户登录
需求:用户输入账号和密码,后端进行校验密码是否正确.
如果正确,跳转到首页,首页显示当前登录用户的用户名如果错误,前端进行用户告知.后续在访问首页,可以获取到登录用户信息.
2.1 接口文档
需求分析:
用户输入账号和密码,后端进行校验密码是否正确.
如果正确,跳转到首页,首页显示当前登录用户的用户名如果错误,前端进行用户告知.后续在访问首页,可以获取到登录用户信息.
登录页面
接口定义:
<code>请求路径: /user/login
请求方式: POST
接口描述: 校验账号和密码的正确性.
请求参数:
参数名 | 类型 | 是否必须 | 备注 |
---|---|---|---|
userName | String | 是 | 校验的账号 |
password | String | 是 | 校验的密码 |
响应数据:
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">code>
<head>
<meta charset="UTF-8">code>
<title>登录页面</title>
</head>
<body>
<h1>用户登录</h1>
用户名:<input name="userName" type="text" id="userName"><br>code>
密码:<input name="password" type="password" id="password"><br>code>
<input type="button" value="登录" onclick="login()">code>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>code>
<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.
比如我们去银行办理业务,有三种可能:
银行没开门忘记带身份证了,业务办理失败证件携带齐全,业务办理成功
第一种就是接口返回了错误信息,第二种就是业务逻辑返回false,第三种就是业务逻辑返回true.
页面跳转的三种方式:
window.location.href=index.htmlwindow.location.assign(“index.html”)window.location.replace(“index.html”)
我们一般把window省略.1,2是等价的,在进入新的页面之后,都可以回退回上一个页面,而3无法回退到上一个页面.
2.2.2 首页
<!doctype html>
<html lang="en">code>
<head>
<meta charset="UTF-8">code>
<meta name="viewport"code>
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">code>
<meta http-equiv="X-UA-Compatible" content="ie=edge">code>
<title>用户登录首页</title>
</head>
<body>
登录人: <span id="loginUser"></span>code>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>code>
<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;
}
//密码和账号都正确,设置session
session.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 接口文档
需求分析:
提交留言:用户输⼊留言信息之后,后端需要把留言信息保存起来展示留言:页面展示时,需要从后端获取到所有的留言信息
接口定义:
获取全部留言
全部留言信息,我们用List来表示,可以用JSON来描述这个List数据.
请求:
<code>GET /message/getList
响应:JSON格式
[
{
"from": "黑猫",
"to": "白猫",
"message": "喵"
},{
"from": "黑狗",
"to": "白狗",
"message": "汪"
},
//...
]
浏览器给服务器发送⼀个GET /message/getList
这样的请求,就能返回当前⼀共有哪些留言记录.结果以json的格式返回过来.发表新留言
请求:body也为JSON格式.
POST /message/publish
{
"from": "黑猫",
"to": "白猫",
"message": "喵"
}
响应:JSON格式.
{
ok: 1
}
我们期望浏览器给服务器发送⼀个POST /message/publish
这样的请求,就能把当前的留言提交给服务器.
3.2 前端代码
<!DOCTYPE html>
<html lang="en">code>
<head>
<meta charset="UTF-8">code>
<meta name="viewport" content="width=device-width, initial-scale=1.0">code>
<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">code>
<h1>留言板</h1>
<p class="grey">输入后点击提交, 会将信息显示下方空白处</p>code>
<div class="row">code>
<span>谁:</span> <input type="text" name="" id="from">code>
</div>
<div class="row">code>
<span>对谁:</span> <input type="text" name="" id="to">code>
</div>
<div class="row">code>
<span>说什么:</span> <input type="text" name="" id="say">code>
</div>
<input type="button" value="提交" id="submit" onclick="submit()">code>
<!-- <div>A 对 B 说: hello</div> -->
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>code>
<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
}
//添加失败,返回false
return 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的开发.
引入依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
使用
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 接口文档
需求;
登录:用户输入账号和密码完成登录功能.
列表展示:展示图书接口定义
登录接口
<code>[URL]
POST /user/login
[请求参数]
name=admin&password=admin
[响应]
true //账号密码验证成功
false//账号密码验证失败
图书列表展示
[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">code>
<head>
<meta charset="UTF-8">code>
<meta name="viewport" content="width=device-width, initial-scale=1.0">code>
<title>Document</title>
<link rel="stylesheet" href="css/bootstrap.min.css">code>
<link rel="stylesheet" href="css/login.css">code>
<script type="text/javascript" src="js/jquery.min.js"></script>code>
</head>
<body>
<div class="container-login">code>
<div class="container-pic">code>
<img src="pic/computer.png" width="350px">code>
</div>
<div class="login-dialog">code>
<h3>登陆</h3>
<div class="row">code>
<span>用户名</span>
<input type="text" name="userName" id="userName" class="form-control">code>
</div>
<div class="row">code>
<span>密码</span>
<input type="password" name="password" id="password" class="form-control">code>
</div>
<div class="row">code>
<button type="button" class="btn btn-info btn-lg" onclick="login()">登录</button>code>
</div>
</div>
</div>
<script src="js/jquery.min.js"></script>code>
<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">code>
<head>
<meta charset="UTF-8">code>
<meta name="viewport" content="width=device-width, initial-scale=1.0">code>
<title>图书列表展示</title>
<link rel="stylesheet" href="css/bootstrap.min.css">code>
<link rel="stylesheet" href="css/list.css">code>
<script type="text/javascript" src="js/jquery.min.js"></script>code>
<script type="text/javascript" src="js/bootstrap.min.js"></script>code>
<script src="js/jq-paginator.js"></script>code>
</head>
<body>
<div class="bookContainer">code>
<h2>图书列表展示</h2>
<div class="navbar-justify-between">code>
<div>
<button class="btn btn-outline-info" type="button" onclick="location.href='book_add.html'">添加图书</button>code>
<button class="btn btn-outline-info" type="button" onclick="batchDelete()">批量删除</button>code>
</div>
</div>
<table>
<thead>
<tr>
<td>选择</td>
<td class="width100">图书ID</td>code>
<td>书名</td>
<td>作者</td>
<td>数量</td>
<td>定价</td>
<td>出版社</td>
<td>状态</td>
<td class="width200">操作</td>code>
</tr>
</thead>
<tbody>
</tbody>
</table>
<div class="demo">code>
<ul id="pageContainer" class="pagination justify-content-center"></ul>code>
</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>';code>
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">';code>
finalHtml += '<a href="book_update.html?bookId=' + book.id + '">修改</a>';code>
finalHtml += '<a href="javascript:void(0)"οnclick="deleteBook(' + book.id + ')">删除</a>';code>
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>',code>
prev: '<li class="page-item"><a class="page-link" href="javascript:void(0);">上一页<\/a><\/li>',code>
next: '<li class="page-item"><a class="page-link" href="javascript:void(0);">下一页<\/a><\/li>',code>
last: '<li class="page-item"><a class="page-link" href="javascript:void(0);">最后一页<\/a><\/li>',code>
page: '<li class="page-item"><a class="page-link" href="javascript:void(0);">{ {page}}<\/a><\/li>',code>
//页面初始化和页码点击时都会执行
onPageChange: function (page, type) {
console.log("第"+page+"页, 类型:"+type);
}
});
function deleteBook(id) {
var isDelete = confirm("确认删除?");
if (isDelete) {
//删除图书
alert("删除成功");
}
}
function batchDelete() {
var isDelete = confirm("确认批量删除?");
if (isDelete) {
//获取复选框的id
var ids = [];
$("input:checkbox[name='selectBook']:checked").each(function () { code>
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后端开发者,又有了一种新的分层架构:把整体架构分为表现层、业务逻辑层和数据层.这种分层方式也称之为"三层架构".
表现层:就是展示数据结果和接受用户指令(接收参数和返回结果)的,是最靠近用户的⼀层;业务逻辑层:负责处理业务逻辑,里面有复杂业务的具体实现(拿到参数之后进行方法的具体实现);数据层:负责存储和管理与应用程序相关的数据(比如数据库交互)
4.2 具体在项目中的体现
在我们创建Spring项目中,具体对分层的实现就是创建一个一个不同的目录,把代码分层管理起来.其中不同层面的目录一般用以下的命名方式:
• Controller:控制层。接收前端发送的请求,对请求进行处理,并响应数据。
• Service:业务逻辑层。处理具体的业务逻辑。
• Dao:数据访问层,也称为持久层。负责数据访问操作,包括数据的增、删、改、查.
• Model: 用于存储对实物属性的描述
下面我们对之前的图书管理的代码进行拆分重构:
表现层
<code>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模式强调数据和视图分离,将数据展示和数据处理分开,通过控制器对两者进行组合
三层架构强调不同维度数据处理的高内聚和低耦合,将交互界面,业务处理和数据库操作的逻辑分开.
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。