[Spring] Spring Web MVC案例实战

news2024/11/25 20:23:36

🌸个人主页: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;
        }
        //密码和账号都正确,设置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 接口文档

需求分析:

  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
        }
        //添加失败,返回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的开发.

  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) {
                    //获取复选框的id
                    var 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.coloradmin.cn/o/1930227.html

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

相关文章

【堆 优先队列 第k大】2551. 将珠子放入背包中

本文涉及知识点 堆 优先队列 第k大 LeetCode2551. 将珠子放入背包中 你有 k 个背包。给你一个下标从 0 开始的整数数组 weights &#xff0c;其中 weights[i] 是第 i 个珠子的重量。同时给你整数 k 。 请你按照如下规则将所有的珠子放进 k 个背包。 没有背包是空的。 如果第…

数据采集监控平台:挖掘数据价值 高效高速生产!

在当今数字化的时代&#xff0c;数据已成为企业非常宝贵的资产之一。然而&#xff0c;要充分发挥数据的潜力&#xff0c;离不开一个强大的数据采集监控平台&#xff0c;尤其是生产制造行业。它不仅是数据的收集者&#xff0c;更是洞察生产的智慧之眼&#xff0c;高效高速处理产…

韦东山嵌入式linux系列-驱动进化之路:总线设备驱动模型-课后作业

在内核源码中搜索 platform_device_register 可以得到很多驱动&#xff0c;选择 一个作为例子&#xff1a; ① 确定它的名字 ② 根据它的名字找到对应的 platform_driver ③ 进入 platform_device_register/platform_driver_register 内部&#xff0c;分析 dev 和 drv 的匹配过…

[WUSTCTF2020]level4题解 入土为安的第三天

二叉树 Practice my Data Structure code..... Typing....Struct.....char....*left....*right............emmmmm...OK! Traversal! Traversal type 1:2f0t02T{hcsiI_SwA__r7Ee} Traversal type 2:20f0Th{2tsIS_icArE}e7__w Traversal type 3: //type3(&x[22]); No w…

Ubuntu/Linux 安装ITKSnap

文章目录 1. 安装ITKSnap1.1 下载后安装 2.进入opt文件夹改名3. 更改启动项4. 创建硬链接5. 添加桌面启动方式6. 即可使用 1. 安装ITKSnap http://www.itksnap.org/pmwiki/pmwiki.php?nMain.HomePage 1.1 下载后安装 找到下载的文件夹&#xff0c;文件夹内打开terminal。复…

Windows系统中MySQL的安装和卸载(详细包含msi和zip下载方式,以及完全卸载方法,易出现问题及解决方案等)

MySQL的安装: 第一种:msi安装(交简单,但是不能自定义安装路径) 下载地址:https://dev.mysql.com/downloads/installer/ 选择历史版本 选择安装版本,这里我选择的是8.0.37的版本,然后点击Download下载离线安装包 如下图即为下载好的版本,双击打开安装 出现如下情况,…

Git操纵本地仓库和远程仓库

git是一个代码托管的平台&#xff0c;我们可以对我们的代码进行分支 推送提交 打标签等等操作&#xff0c;而且git使用过程中也是支持一些linux语言的 比如cd呀 touch mkdir啊等等等 git的具体安装过程就不再赘述 我个人认为 好多东西就是 代码也好 文字 文档 也好&…

Centos7 桌面版安装ToDesk远程控制;linux安装Todesk远程控制;linux远程桌面方案

获取安装包&#xff1a; https://dl.todesk.com/linux/todesk-v4.7.2.0-c7-x86_64.rpm 得到 这个文件就是linux的安装包&#xff0c;后缀名就是rpm&#xff0c;类似windows下的exe 将此文件以你的方式&#xff0c;放到linux中&#xff0c;放哪都行 我使用的FileZila&#xf…

新手教学系列——简单的服务配置项集中管理

前言 在开发和运维过程中&#xff0c;配置管理是一个非常重要但经常被忽视的环节。常用的配置文件格式包括env、ini和yaml等&#xff0c;它们非常适合模块级别的系统配置&#xff0c;尤其是一些敏感信息的配置&#xff0c;例如数据库连接字符串和密码等。但是&#xff0c;对于…

【系统架构设计】数据库系统(一)

数据库系统&#xff08;一&#xff09; 数据库模式与范式数据库的结构与模式数据模型关系代数数据的规范化反规范化 数据库设计事务管理备份与恢复分布式数据库系统数据仓库数据挖掘NoSQL大数据 数据库模式与范式 数据库的结构与模式 数据库技术中采用分级的方法将数据库的结…

释放DOE的能量,快速确定最佳工艺设置,节省时间、成本和资源

您是否希望降低成本、提高生产效率&#xff0c;并最大限度地减少行业对环境的影响&#xff1f; 所有行业&#xff0c;尤其是钢铁、铝、水泥和石化等能源密集型行业&#xff0c;都面临着应对这些挑战的持续压力。供应链压力、可持续发展、严格的监管环境、日益增长的消费者预期…

Transformer中高级位置编码的介绍和比较:Linear Rope、NTK、YaRN、CoPE

在处理诸如文本之类的序列时&#xff0c;排序信息显然是至关重要的。为了结合排序信息而不是将序列视为集合&#xff0c;对位置信息进行编码是至关重要的。位置编码通过为每个位置分配嵌入向量并将其添加到相应的标记表示来实现这一点。绝对和相对位置编码是最常见的两种位置编…

外贸行业汽车销售配件展示企业网站源码系统 带完整的源代码包以及搭建教程

系统概述 随着全球贸易的不断深化&#xff0c;外贸行业对于高效、专业的网站需求日益凸显。特别是对于汽车销售配件企业而言&#xff0c;一个功能全面、展示效果出色的网站源码系统&#xff0c;无疑是企业开拓海外市场、提升品牌形象的关键。本文将详细介绍一款专为外贸行业汽…

【Linux】文件管理常用命令【超详细】

文章目录 预防rm事故-血的教训&#x1f622;1. 使用别名&#xff1a;2. 启用回收站&#xff1a;3. 只读文件系统&#xff1a; 一、文件管理1.1 touch-文件创建1.2 rm-文件删除1.3 mkdir-目录创建1.4 rmdir-目录删除1.5 pwd-显示当前目录1.6 cd-切换当前目录1.7 ls-列出文件和目…

鸿蒙语言基础类库:【@system.device (设备信息)】

设备信息 说明&#xff1a; 从API Version 6开始&#xff0c;该接口不再维护&#xff0c;推荐使用新接口[ohos.deviceInfo]进行设备信息查询。本模块首批接口从API version 3开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 import dev…

AI绘画Stable Diffusion 零基础入门 —AI 绘画原理与工具介绍,万字解析AI绘画的使用教程

大家好&#xff0c;我是设计师阿威 想要入门 AI 绘画&#xff0c;首先需要了解它的原理是什么样的。 其实很早就已经有人基于深度学习模型展开了对图像生成的研究了&#xff0c;但在那时&#xff0c;生成的图像分辨率和内容都非常抽象。 直到近两年&#xff0c;AI 产出的图像…

[openwrt-21.02]mt7981开启mwan3功能ping出现unreachable 问题分析及解决方案

mwan3 提供以下功能和能力 基于数值权重分配的出站 WAN 流量负载均衡或使用多个 WAN 接口进行故障转移 使用重复测试监控每个 WAN 连接,如果第一个 WAN 接口失去连接,则可以自动将出站流量路由到另一个 WAN 接口 创建出站流量规则以自定义哪些出站连接应使用哪个 WAN 接口(…

白门楼 下 | 第13集 | 曹操口头禅:故戏之耳 | 逐鹿群雄 | 三国演义

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 &#x1f4cc;这篇博客分享的是《三国演义》文学剧本第Ⅰ部分《群雄逐鹿》的第13集《白门楼 下》的经典语句和文学剧本全集台词 文章目录 1.经典语句2.文学剧本台词 …

防火墙---带宽管理

防火墙的带宽管理&#xff1a;是指对防火墙设备的带宽进行管理和控制&#xff0c;以确保网络流量的合理分配和优化网络性能 带宽管理&#xff1a;是指限制网络流量的速率或控制网络流量的优先级&#xff0c;以确保网络的性能和可用性 核心&#xff1a; 带宽限制&#xff1a;…

环形数组复习

普通储存数据 接收数据 先要有个 缓存区 通常先建立一个数组 来保存数据 缓存区内存 如何分配和释放 此时 一包数据为 5字节 缓冲区为 17字节 方法一&#xff1a;每次清空缓冲区&#xff0c;重头开始存放数据 第一次 存放在 字节1-5 然后分析读取这次数据 后 先清除B…