当前位置: 首页 > article >正文

Spring MVC:综合练习 - 深刻理解前后端交互过程

目录

1. Lombok

1.1 引入 lombok 依赖

1.1.1 通过 Maven 仓库引 lombok 依赖

 1.1.2 通过插件引入 lombok 依赖

1.2 @Data

1.3 其他注解

2. 接口文档

2.1 接口(api)

2.2 接口文档

3. 综合练习 - 加法计算器

3.1 定义接口文档

3.2 准备工作 - 前端代码

3.3 后端代码

3.4 运行测试

4. 综合练习 - 用户登录

4.1 定义接口文档

​编辑

4.2 准备工作 - 使用 Ajax 编写前端代码

4.3 后端代码

4.3.1 校验接口

4.3.2 返回用户信息接口

4.4 运行测试

 5. 综合练习 - 留言板

5.1 接口文档

5.2 后端代码

5.2.1 接口一: 用户发表留言

5.2.2 接口二: 获取留言信息

5.3 前端代码

5.4 运行测试

6. 综合练习 - 图书管理系统

6.1 接口文档

6.2 后端代码

6.2.1 接口一: 登录验证

6.2.2 接口二: 提供图书列表

6.3 前端代码

6.3.1 登录验证页面

6.3.2 图书列表展示页面

6.4 运行测试

7. 应用分层

7.1 为什么要应用分层

7.2 如何分层

7.2.1 对图书系统的代码进行分层


1. Lombok

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

第一步, 将 lombok 的依赖导入 pom 文件中.

导入 lombok 的依赖有两种方式:

  1. 在 Maven 仓库中找 lombok 依赖(通用)
  2. 通过插件引 lombok 依赖(非通用)

1.1 引入 lombok 依赖

1.1.1 通过 Maven 仓库引 lombok 依赖

Maven 仓库: https://mvnrepository.com/

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.30</version>
    <scope>provided</scope>
</dependency>

 1.1.2 通过插件引入 lombok 依赖

1.2 @Data

导入 lombok 依赖后, 我们就可以对类使用 @Data 注解, 这样就不用对类中的私用属性定义 setter 和 getter 方法, 也不用重写 toString, hashCode, equals 等方法, @Data 注解会帮我们自动生成.

因此, @Data 可以显著减少代码量, 使代码更加简洁易读.

我们可以通过 idea 反编译的文件来观察:

 并且, 在类外也可以成功使用 @Data 生成的这些方法:

1.3 其他注解

@Data 虽然很方便, 但是在某些特定的场景下, 我们并不希望生成所有的方法, 比如: 我们不想重写 toString(), 就是像打印出内存. 那么, 此时 @Data 显然就不适用了.

其实, @Data = @Getter + @Setter + @ToString + @EqualsAndHashCode + @RequiredArgsConstructor  + @NoArgsConstructor

因此, 当我们想生成某个特定的方法时, 就可以使用某个特定的注解.

例如, 只对类中的属性生成 get 和 set 方法:

当使用这些注解作用于某个属性上时, 那就只对这个属性生成 get 和 set 方法:

2. 接口文档

2.1 接口(api)

接口, 又称为 api, 这里的接口并非 Java 中的 interface.

  • 通俗来说,api 就是别人写的一些函数/类,你直接拿过来就能用: api是一个广义的概念,操作系统会提供api、标准库会提供api、第三方库会提供api、其他各种开源项目会提供api、甚至工作中项目组给你的代码中也会提供api。
  • api也可以理解为,别人给你提供的库/程序,你都能用来干啥: 举个例子:对于同班同学,你可以给他在微信上发消息、可以问他题、可以和他聊天、....,这是你同学向你提供的api;你谈了个对象,你可以和你的对象亲亲抱抱举高高....,这是你对象向你提供的api。
  • 而基于api,你可以用来编程(api的目的就是用于编程): 比如接上例,基于你同学或者对象向你提供的api,你可以做出规划(编程):周末约同学打球;周末约对象看电影......

而对于Java程序猿的我们,我们可以使用标准库向我们提供的api去编程,比如ArrayList、StringBuffer、.......

2.2 接口文档

一个项目的开发, 大概总体分为两个大步骤:

步骤1: 需求阶段

  1. 用户提出需求
  2. 产品经理编写需求文档(需求文档中包含了相关功能)
  3. 评审需求文档

步骤2: 开发阶段

  1. 提出开发方案并进行评审
  2. 排期(接口文档什么时候提供, 什么时候开发完成, 什么时候自测完成, 时候时候联调, 什么时候上线)
  3. 提供接口文档以及相应依赖
  4. 前后端各自进行开发

综上, 需求开发前, 需要先定义接口文档.

写接口时, 前后端需要严格遵守接口文档中的要求去实现, 并且后续开发时, 前后端也会严格按照文档去调用相关 api.

接口文档, 通俗来说就是一个说明书, 它规定了 "前后端交互的接口" 该如何去实现. 比如: 接口文档中会说明某个方法的方法功能, 方法名称, 参数名称, 参数类型, 返回值类型....... 

下图就是 Java 中 String 的接口文档, 我们调用相关 api 时, 要严格遵守文档中的要求去调用:

因此, 接口文档不能随意修改, 如果对接口文档进行了修改, 那么一定要告知使用方.(接口文档由服务提供方去写的, 修改时也要让提供方去修改, 使用方不要去修改)


3. 综合练习 - 加法计算器

结合之前对 Spring 和前端的学习, 这里进行一下小练习.

第一个练习, 实现一个加法计算器:       

3.1 定义接口文档

开发计算器先, 需要约定好前后端交互接口的格式, 这里简单制定的接口文档如下:

3.2 准备工作 - 前端代码

我们根据需求文档, 编写出前端代码. 

这里对前端代码进行一下简单解释:

其中, action 的值, 就是前端所要调用的后端方法的路径.

<!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="get">
        <h1>计算器</h1>
        数字1:<input name="num1" type="text"><br>
        数字2:<input name="num2" type="text"><br>
        <input type="submit" value=" 点击相加 ">
    </form>
</body>

</html>

3.3 后端代码

我们后端也是一样, 需要根据接口文档的要求, 写出实现该计算功能的方法.

当前端调用我们的方法时, 我们要返回正确的结果.(前端调用后端方法, 后端服务器返回响应) 

实现加法功能的代码比较简单, 这里就不赘述了, 本例主要是想告诉大家前后端交互的过程.

@RestController
@RequestMapping("/calc")
public class CalcController {
    // 实现加法
    @RequestMapping("/sum")
    public String sum(Integer num1, Integer num2) {
        if(num1 == null || num2 == null) {
            return "输入有误, 请重新输入!!";
        }
        int ret = num1 + num2;
        return "<h1>计算结果为: " + ret + "</h1>";
    }
}

3.4 运行测试


4. 综合练习 - 用户登录

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

  1. 如果正确, 则跳转到首页. 首页显示当前登录用户.
  2. 如果不正确, 前端告知用户密码错误.
  3. 后续再访问首页, 可以获取到用户登录信息.

4.1 定义接口文档

接口文档中的请求方式 GET 和 POST 都可以, 只是语义上的区别.

4.2 准备工作 - 使用 Ajax 编写前端代码

在上个练习中, 我们使用的是 form 标签, 使用 action 属性指定数据提交的 URL(后端接口地址), 但这样会导致页面跳转.

但是, 如果使用 Ajax 提交表单数据, 就不会导致页面跳转. 这是因为 AJAX 的核心目的是在不重新加载整个页面的情况下, 与服务器进行异步通信并以 "增量更新" 的形式更新页面的局部内容.

借助 JQuery 实现 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="js/jquery-3.7.1.min.js"></script>
  <script>
    // 点击登录按钮, 调用 login 方法
    // 进而调用 Ajax
    function login() {
      // 发起 Ajax 的语法: $.ajax(参数) 参数是一个对象: { 对象的内容 }
      $.ajax({
        url: "/user/login",
        type: "post",
        data: {
          userName: $("#userName").val(),
          password: $("#password").val()
        },
        success: function (result) {
          if(result) {
            // 密码正确
            location.href = "index.html";
            // location.assign("index.html");
          }else {
            alert("密码错误!!");
          }
        }
      });
    }
  </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="js/jquery-3.7.1.min.js"></script>
    <script>
        // 从上往下自动调用 Ajax
        $.ajax({
            url: "/user/getLoginUser",
            type: "get",
            success: function (userName) {
                // 选中 span 标签, 对 span 进行赋值
                // span 标签使用 text 赋值
                $("#loginUser").text(userName);
            }
        });
    </script>
</body>

</html>

4.3 后端代码

4.3.1 校验接口

进行账号密码的校验很简单, 比较两个字符串是否相等即可.

需要注意的是, 当校验成功后, 我们还需要将用户的用户名信息存到 Session 中, 以便后续根据 sessionid 查找 session 获取并返回用户信息.

比较账号密码时, 我们可以使用 Spring 第三方库提供的接口 StringUtils.hasLength() (字符串是否有长度)来判断用户输入的账号密码是否合法.

4.3.2 返回用户信息接口

由于我们已经将用户信息存到 session 中, 因此只需要根据 Sessionid 获取 Session, 再调用getAttribute 方法来获取用户信息即可.

@RestController
@RequestMapping("/user")
public class UserController {

    // 校验接口
    @RequestMapping("/login")
    public boolean login(String userName, String password, HttpSession session) {
        // 处理非法输入
//        if(userName == "" || userName == null || password == "" || password == null) {
//            return false;
//        }
        // String 提供的 api => 检验字符串是否有长度
        if(!StringUtils.hasLength(userName) || !StringUtils.hasLength(password)) {
            return false;
        }
        // 目前还未学习数据库相关操作
        // 这里先假设 userName = password = 1111
        // 使用字符串常量调用 equals 方法, 避免空指针异常
        if("1111".equals(userName) && "1111".equals(password)) {
            // 若登录成功, 则将用户信息存储在 Session 中
            session.setAttribute("userName", userName);
            return true;
        }
        return false;
    }

    // 查询用户登录接口
    @RequestMapping("/getLoginUser")
    public String getLoginUser(HttpSession session) {
        return (String)session.getAttribute("userName");
    }

}

4.4 运行测试

上文说到, 返回用户信息, 本质上是通过 Sessionid 获取 session, 从而获取用户名的. 

因此, 即使我们登陆成功, 但如果修改 sessionid 的值, 那么也是不能查找到用户信息的, 也就是在第二个页面中不会返回用户名:


 5. 综合练习 - 留言板

需求: 

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

5.1 接口文档

发表留言接口:

获取留言接口:

5.2 后端代码

5.2.1 接口一: 用户发表留言

由于接口文档要求请求 Body 和响应 Body 均为 JSON 格式, 因此我们需要保证以下两点:

  1. 确保前端发送来的是 JSON 数据, 我们后端用的也是 JSON 来接收: 接口接收数据时, 使用 @RequestBody 将接口参数和请求中的 JSON 绑定起来. 
  2. 确保我们后端返回的是 JSON 数据, 并且响应的 Content-Type 为 application/json: 接口返回数据时, 需要保证返回的是 JSON 数据.

以上两点是必须要保证的, 否则结果将不符合预期. 

使得接口返回的是一个 JSON 数据, 有以下两种方式:

  1. 返回的是一个对象(Spring 会自动序列化为 JSON 格式并将 Content-Type 转换为 application-json)
  2. 返回字符串, 但是需要手动将 Content-Type 设置为 application-json(通过 @RequestMapping 的 produces 参数来设置)

这里采用第二种方式.

注意, 返回的数据一定要是 JSON 的格式, 浏览器收到后才能进行正确的解析:

JSON 数据格式:
{
  "key": "value"
}

由于我们后端还需要返回留言信息, 因此, 当后端接口接收到留言时, 要对留言进行保存.

可以使用 List 将收到的所有的留言信息保存在后端服务器上.

并且, 可以将留言封装为一个类 MessageInfo, 每一条留言就是一个对象. 这样, 就可以将留言信息存到 List 中了(List<MessageInfo>).

注意: 当后端返回的数据的数据格式为 JSON 时, 那么前端收到后, 就可以直接把这个数据当做一个对象使用, 不用再进行转换.

因此, 一定场景需求下, 后端返回 JSON 格式的数据, 是对前端有帮助的.

5.2.2 接口二: 获取留言信息

直接返回 List 即可, 返回的 List 也是一个对象, Spring 会自动帮我们转换为 JSON 格式.

// 留言
@Data
public class MessageInfo {
    private String from;
    private String to;
    private String message;
}
@RestController
@RequestMapping("/message")
public class MessageController {
    // 保存留言板信息
    List<MessageInfo> list = new ArrayList<>();
    // 接口一: 用户发表留言
    @RequestMapping(value = "/publish", produces = "application/json")
    public String publish(@RequestBody MessageInfo messageInfo) {
        if(!StringUtils.hasLength(messageInfo.getFrom())
                || !StringUtils.hasLength(messageInfo.getTo())
                || !StringUtils.hasLength(messageInfo.getMessage())) {
            return "{\"ok\": 0}";
        }
        list.add(messageInfo);
        return "\"ok\": " + 1;
    }
    // 接口二: 获取留言信息
    @RequestMapping("/getList")
    public List<MessageInfo> getList() {
        return list;
    }

5.3 前端代码

在前端中, 我们也要确保以下两个要点:

  1. 确保请求中的 Body 为 JSON 格式的数据
  2. 确保请求的 Content-Type 为 application/json

也就是说, Body 中内容的格式要和 Content-Type 的格式对应上. 比如: Body 是 JSON, 但 Content-Type 不是 JSON, 那就会发生错误. 

Ajax 默认传的是一个对象(非 JSON), 若要要传 JSON, 则需要调用 JSON.stringify 方法进行转换:

当前端收到用户的留言后, 要将留言展示在界面上, 并且调用后端接口, 让后端存储起来.

这里仍然使用 Ajax 进行数据传输.

前后端参数的对应关系如下:

每次刷新页面时, 浏览器都会重新加载前端代码, 包括 HTML, CSS 和 JavaScript. 这个过程会导致页面的内容被重置.

在之前的练习中, 我们都未使用后端存储用户的输入信息, 因此当用户刷新浏览器页面后, 原来的界面就会被重置.

但是, 在本例中, 我们已经将留言存储在了后端服务器上, 因此, 我们可以通过前端代码调用后端接口, 通过后端获取列表中的留言信息, 将留言展示到界面上.

因此, 就算用户刷新页面, 但由于后端服务器没有重新启动, 所以存储在后端的数据也不会丢失. 

故, 前段代码中应两次调用后端接口:

  1. 用户留言后, 前端将留言信息发送给后端.
  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="js/jquery-3.7.1.min.js"></script>
    <script>
        load();
        // 用户刷新时, 就会调用这个方法
        // 前端就会从后端获取留言列表, 展示在页面上
        function load() {
            $.ajax({
                url: "/message/getList",
                type: "post",
                success: function(messageList) {
                    if(messageList != null && messageList.length > 0) {
                        for(var m of messageList) {
                           var divE = "<div>"+ m.from +"对" + m.to + "说:" + m.message+"</div>";
                             //3. 把节点添加到页面上    
                            $(".container").append(divE); 
                        }
                    }
               }
            });
        }
 
        function submit(){
            //1. 获取留言的内容
            var from = $('#from').val();
            var to = $('#to').val();
            var say = $('#say').val();
            // 判断非法输入
            if (from== '' || to == '' || say == '') {
                return;
            }
            var data = {
              from: from,
              to: to,
              message: say
            };
            $.ajax({
                url: "/message/publish",
                type: "post",
                contentType: "application/json",
                // 对象转 JSON
                data: JSON.stringify(data),
                success: function(result) {                    
                    if(result.ok == 1) {
                        //2. 构造节点
                        var divE = "<div>"+from +"对" + to + "说:" + say+"</div>";
                         //3. 把节点添加到页面上    
                        $(".container").append(divE);
                         //4. 清空输入框的值
                        $('#from').val("");
                        $('#to').val("");
                        $('#say').val("");
                    }else {
                        alert("输入非法数据!!");
                    }
                } 
            });
        }
        
    </script>
</body>

</html>

5.4 运行测试

之所以刷新页面后, 数据没有丢失, 是因为页面刷新时, 前端调用了后端接口, 获取了存储的留言信息, 并重新进行了展示, 因此用户刷新后也能看到之前的留言.

但是, 一旦后端服务器重启了, 那么意味着存储在后端 List 对象中的留言信息也会被销毁, 刷新后就看不到了.


6. 综合练习 - 图书管理系统

我们先实现以下两个简单页面: 

6.1 接口文档

6.2 后端代码

根据界面图可知, 后端要实现以下两个接口:

  1. 接口一: 登录验证
  2. 接口二: 提供图书列表

并且, 在实现这两个接口前, 需要先创建一个图书类.

通过观察页面内容, 我们可以得知类中一些的字段, 如:

id, 图书名, 作者, 库存数量, 价格, 出版社, 状态.

@Data
public class BookInfo {
    private long bookId;
    private String bookName;
    private String author;
    private long num;
    private BigDecimal price;
    private String publish;
    // 状态信息, 习惯上使用数字
    private int status; // 1 -> 可借阅, 2 -> 不可借阅
    // 图书状态的中文藐视
    // 开发中, 一般交给前端处理. 由于学习, 在后端这里直接就处理了
    private String statusCN;
}

6.2.1 接口一: 登录验证

这一接口和之前的 "用户登录" 接口是一致的:

并且由于还未学习数据库相关操作, 这里暂且把账号密码写死, 都为: "1111"

@RequestMapping("/user")
@RestController
public class UserController {
    // 登录验证接口
    @RequestMapping("/login")
    public boolean login(String name, String password, HttpSession session) {
        if(!StringUtils.hasLength(name) || !StringUtils.hasLength(password)) {
            return false;
        }
        // 由于未学习数据库, 假设: name = password = "1111"
        if("1111".equals(name) && "1111".equals(password)) {
            session.setAttribute("userName", name);
            session.setAttribute("password", password);
            return true;
        }
        return false;
    }
}

6.2.2 接口二: 提供图书列表

登录成功后, 将进入图书列表页面:

此时, 我们后端需要从数据库中查询图书信息, 将查询到的图书, 存储到 List 中, 并返回给前端.

由于, 我们还未学习数据库相关操作, 这里也暂且 mock 数据.

@RestController
@RequestMapping("/book")
// 获取图书列表
public class BookController {
    @RequestMapping("/getList")
    public List<BookInfo> getList(){
        // 还未学习数据库操作, 这里暂且 mock 数据
        List<BookInfo> bookList = mockData();
        // 对结果进行二次处理
        for(BookInfo bookInfo : bookList) {
            if(bookInfo.getStatus() == 1) {
                bookInfo.setStatusCN("可借阅");
            }else {
                bookInfo.setStatusCN("不可借阅");
            }
        }
        return bookList;
    }

    // mock 图书数据
    private List<BookInfo> mockData() {
        List<BookInfo> bookList = new ArrayList<>();
        for (int i = 1; i <= 5; i++) {
            BookInfo bookInfo = new BookInfo();
            bookInfo.setBookId(i);
            bookInfo.setAuthor("作者" + i);
            bookInfo.setBookName("图书" + i);
            bookInfo.setPublish("出版社" + i);
            bookInfo.setNum(new Random().nextInt(100));
            bookInfo.setPrice(BigDecimal.valueOf(new Random().nextInt(100)));
            bookInfo.setStatus(i % 5 == 0 ? 2 : 1);
            bookList.add(bookInfo);
        }
        return bookList;
    }
}

6.3 前端代码

很明显, 前端需要设计两个页面, 并且需要调用两次不同的后端接口:

  1. 登录验证页面 => 调用后端的登录验证接口
  2. 图书展示页面(登录成功后, 跳转到该页面) => 调用后端的获取图书列表接口

6.3.1 登录验证页面

该页面中, 用户输入账号密码, 并点击提交按钮后, 前端应调用后端登录验证接口, 且传递的数据格式为普通对象.

后端会返回 true(验证成功) 或者 false(验证失败), 前端应根据返回接口做出进一步处理:

  1. 若验证成功 => 跳转图书展示页面
  2.  若验证失败 => 对用户进行相关提示(这里使用 alert 弹框提示)
<!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({
                url: "/user/login",
                type: "post",
                data: {
                    name: $("#userName").val(),
                    password: $("#password").val()
                },
                success: function(result) {
                    if(result) {
                        location.href = "book_list.html";   
                    }else {
                        alert("账号密码错误!!");
                    }
                }
            });

        }
    </script>
</body>

</html>

6.3.2 图书列表展示页面

用户登录成功后, 跳转到该页面.

本页面的前端代码, 需要调用后端接口, 获取数据库中的图书信息, 并且后端返回的 List 中的图书信息, 拼接到一起, 展示到页面上:

这里给大家介绍一个 JQuery 的方法:

需要注意的是, 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>
                <tr>
                    <td><input type="checkbox" name="selectBook" value="1" id="selectBook" class="book-select"></td>
                    <td>1</td>
                    <td>大秦帝国第一册</td>
                    <td>我是作者</td>
                    <td>23</td>
                    <td>33.00</td>
                    <td>北京出版社</td>
                    <td>可借阅</td>
                    <td>
                        <div class="op">
                            <a href="book_update.html?bookId=1">修改</a>
                            <a href="javascript:void(0)" onclick="deleteBook(1)">删除</a>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td><input type="checkbox" name="selectBook" value="1" id="selectBook" class="book-select"></td>
                    <td>2</td>
                    <td>大秦帝国第二册</td>
                    <td>我是作者</td>
                    <td>23</td>
                    <td>33.00</td>
                    <td>北京出版社</td>
                    <td>可借阅</td>
                    <td>
                        <div class="op">
                            <a href="book_update.html?bookId=2">修改</a>
                            <a href="javascript:void(0)" onclick="deleteBook(2)">删除</a>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td><input type="checkbox" name="selectBook" value="1" id="selectBook" class="book-select"></td>
                    <td>3</td>
                    <td>大秦帝国第三册</td>
                    <td>我是作者</td>
                    <td>23</td>
                    <td>33.00</td>
                    <td>北京出版社</td>
                    <td>可借阅</td>
                    <td>
                        <div class="op">
                            <a href="book_update.html?bookId=3">修改</a>
                            <a href="javascript:void(0)" onclick="deleteBook(3)">删除</a>
                        </div>
                    </td>
                </tr>
                <tr>
                    <td><input type="checkbox" name="selectBook" value="1" id="selectBook" class="book-select"></td>
                    <td>4</td>
                    <td>大秦帝国第四册</td>
                    <td>我是作者</td>
                    <td>23</td>
                    <td>33.00</td>
                    <td>北京出版社</td>
                    <td>可借阅</td>
                    <td>
                        <div class="op">
                            <a href="book_update.html?bookId=4">修改</a>
                            <a href="javascript:void(0)" onclick="deleteBook(4)">删除</a>
                        </div>
                    </td>
                </tr>
            </tbody>
        </table>

        <div class="demo">
            <ul id="pageContainer" class="pagination justify-content-center"></ul>
        </div>
        <script>

            getBookList();
            function getBookList() {
                $.ajax({
                    url: "/book/getList",
                    type: "get",
                    success: function(bookList) {
                        var newHtml = '';
                        for(var book of bookList) {
                            newHtml += '<tr>';
                            newHtml += '<td><input type="checkbox"name="selectBook" value="' + book.bookId + '" id="selectBook" class="book-select"></td>';
                            newHtml += '<td>' + book.bookId + '</td>';
                            newHtml += '<td>' + book.bookName + '</td>';
                            newHtml += '<td>' + book.author + '</td>';
                            newHtml += '<td>' + book.num + '</td>';
                            newHtml += '<td>' + book.price + '</td>';
                            newHtml += '<td>' + book.publish + '</td>';
                            newHtml += '<td>' + book.statusCN + '</td>';
                            newHtml += '<td><div class="op">';
                            newHtml += '<a href="book_update.html?bookId=' + book.bookId + '">修改</a>';
                            newHtml += '<a href="javascript:void(0)" onclick="deleteBook(' + book.bookId + ')">删除</a>';
                            newHtml += '</div></td></tr>';
                        }
                        // .html => 置换 tbody 标签里面的内容
                        $("tbody").html(newHtml);
                    }
                });
            }
    
            //翻页信息
            $("#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>

6.4 运行测试


7. 应用分层

7.1 为什么要应用分层

实际开发中, 一个项目的代码量是非常大的, 大量逻辑的实现就会使得代码 "杂乱无章".

而应用分层就是将不同类型的代码组织起来, 让他们看起来没有那么杂乱~ 就像一个公司一样, 有后端团队, 前端团队, 人力团队, 财务团队, ....

并且, 对代码进行分类, 有利于看代码, 查找代码, 回忆代码功能. 

这样说的应用分层, 对我们 java 后端开发者来分层的.

7.2 如何分层

主要分为以下三层:

  1. 表现层, 使用 Controller 表示 => 和客户端进行交互. 接收参数, 和返回数据.(最靠近用户)
  2. 业务逻辑层 , 使用 Service 表示 => 对从数据库拿到的数据进行二次处理(业务方面).
  3. 数据层, 使用 Dao 表示 => 数据的管理(存储, 查询)

此外, 我们后端代码中, 还存在很多类, 我们称之为实体类, 使用 model, pojo, entity 来表示.

以上三层是逐级调用的关系: 客户端将请求传给表现层, 表现层接收参数并调用业务逻辑层, 业务逻辑层调用数据层, 数据层去数据库查数据, 查到后将数据返回给业务逻辑层, 业务逻辑层处理数据后返回给表现层, 表现层返回给客户端.

7.2.1 对图书系统的代码进行分层

以三层架构的思想, 对上面的图书系统进行分层:

实体类(用到的类都放到这个包中):

表现层:

业务逻辑层: 

数据层:


END


http://www.kler.cn/a/516036.html

相关文章:

  • skynet 源码阅读 -- 启动主流程
  • 智能化加速标准和协议的更新并推动验证IP(VIP)在芯片设计中的更广泛应用
  • 2025.1.20——二、buuctf BUU UPLOAD COURSE 1 1 文件上传
  • Java基础 (一)
  • ubuntu20.04有亮度调节条但是调节时亮度不变
  • python学opencv|读取图像(四十)掩模:三通道图像的局部覆盖
  • PaSa - 大型语言模型提供支持的高级论文搜索代理
  • 使用KNN实现对鸢尾花数据集或者自定义数据集的的预测
  • 基于JAVA的微信点餐小程序设计与实现(LW+源码+讲解)
  • FCA-FineReport试卷
  • 数据挖掘常用算法模型简介
  • 有关Android Studio的安装与配置并实现helloworld(有jdk的安装与配置)(保姆级教程)
  • 云计算和服务器
  • 软件工程的本质特征
  • 无人机高速无刷动力电机核心设计技术
  • Python 之 Excel 表格常用操作
  • 考研机试:学分绩点
  • linux 扩容
  • MySQL 中开启二进制日志(Binlog)
  • 0164__【GNU】gcc -O编译选项 -Og -O0 -O1 -O2 -O3 -Os
  • three.js+WebGL踩坑经验合集(1):THREE.Line无故消失的元凶
  • c++-------------------------继承
  • 神经网络梯度爆炸的原因及解决方案
  • 10个非常基础的 Javascript 问题
  • Seata进阶全文详解(集成Nacos及SpringCloud配置)
  • web服务器 网站部署的架构