Spring MVC 综合案例

目录

[一. 加法计算器](#一. 加法计算器)

[1. 准备工作](#1. 准备工作)

[2. 约定前后端交互接口](#2. 约定前后端交互接口)

需求分析

接口定义

[3. 服务器端代码](#3. 服务器端代码)

[4. 运行测试](#4. 运行测试)

[二. 用户登录](#二. 用户登录)

[1. 准备工作](#1. 准备工作)

[2. 约定前后端交互接口](#2. 约定前后端交互接口)

需求分析

接口定义

[(1) 登录界面接口](#(1) 登录界面接口)

[(2) 首页接口](#(2) 首页接口)

[3. 服务器端代码](#3. 服务器端代码)

[4. 运行测试](#4. 运行测试)

[三. 留言板](#三. 留言板)

[1. 准备工作](#1. 准备工作)

[2. 约定前后端交互接口](#2. 约定前后端交互接口)

需求分析

接口定义

[3. 服务器端代码](#3. 服务器端代码)

[四. 图书管理系统](#四. 图书管理系统)

[1. 准备工作](#1. 准备工作)

[2. 约定前后端交互接口](#2. 约定前后端交互接口)

需求分析

接口定义

[3. 服务器端代码](#3. 服务器端代码)

登录页面

图书列表

创建图书:

返回图书列表:

[五. lombook 介绍](#五. lombook 介绍)

[六. 应用分层](#六. 应用分层)


一. 加法计算器

1. 准备工作

创建SpringBoot项目, 并引入SpringBoot依赖. 把前端页面的代码放到项目中.

前端代码:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
<form action="calc/sum" method="post">
    <h1>计算器</h1>
    数字1:<input name="num1" type="text"><br>
    数字2:<input name="num2" type="text"><br>
    <input type="submit" value=" 点击相加 ">
</form>
</body>

</html>

2. 约定前后端交互接口

由于现在大多是以 "前后端分离模式" 开发, 前后端代码通常由不同团队进行开发. 所以双方团队在开发之前, 会提前约定好前后端交互方式. 常用 "接口文档" 来描述它. (接口文档 也可以理解为"应用程序的说明书").

需求分析

加法计算器功的能是对两个整数进行相加. 需要客户端提供参与计算的两个数, 服务端返回这两个整数相加的结果.

接口定义

html 复制代码
请求路径: clac / sum

请求方式: GET / POST

接口描述: 计算两个整数相加

请求参数:

响应数据:

html 复制代码
Content-Type: text / html

响应内容: 计算机计算结果: x 

3. 服务器端代码

java 复制代码
@RestController
@RequestMapping("/calc")
public class CalcController {
    @RequestMapping("/sum")
    public String sum(Integer num1, Integer num2) {
        int sum = num1 + num2;
        return "<h1>计算机计算结果: "+sum+"</h1>";
    }
}

4. 运行测试

运行main方法, 启动服务.

访问服务地址: http://127.0.0.1:8080/calc.html

二. 用户登录

1. 准备工作

创建SpringBoot项目, 并引入SpringBoot依赖. 把前端页面的代码放到项目中.

  • index.html (登录界面) 代码:
html 复制代码
<!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>
  • login.html (首页) 代码:
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="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. 约定前后端交互接口

需求分析

(1) 登录页面: 通过账号和密码, 校验输入的账号密码是否正确, 并告知前端.

(2) 首页: 告知前端当前登录用户. 如果当前已有用户登录, 返回登录的账号; 如果没有, 返回空.

接口定义

(1) 登录界面接口

接口定义:

html 复制代码
请求路径: /user/login

请求方式: POST

接口描述: 校验账号密码是否正确.

请求参数:

响应数据:

html 复制代码
Content-Type : text/html
响应内容: 
账号密码正确:true
账号密码错误:false
(2) 首页接口

接口定义:

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

请求参数: 无

响应数据:

java 复制代码
Content-Type:text/html
响应内容: 登录的用户名.

3. 服务器端代码

(1) 登录界面接口

java 复制代码
@RestController
@RequestMapping("/user")
public class LoginController {
    @RequestMapping("/login")
    public boolean login(String userName, String password, HttpSession session) {
        // 账号或密码为空
        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.
}

(2) 首页接口

java 复制代码
@RestController
@RequestMapping("/getLoginUser")
public class getLoginUser {
    @RequestMapping("/")
    public String getLoginUser(HttpSession session) {
        //从Session中获取用户登录信息
        String userName = (String) session.getAttribute("userName");
        //如果用户已经登录, 则直接返回用户名
        if (StringUtils.hasLength(userName)) {
            return userName;
        }
        //否则什么都不返回
        return "";
    }
}

4. 运行测试

验证不成功:

验证成功:

三. 留言板

1. 准备工作

创建SpringBoot项目, 并引入SpringBoot依赖. 把前端页面的代码放到项目中.

html 复制代码
<!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>

2. 约定前后端交互接口

需求分析

后端需要提供两个服务;

(1) 提交留言: 客户输入留言信息之后, 后端要把留言信息保存起来.

(2) 展示留言: 页面展示时, 需要从后端获取到所有的留言信息.

接口定义

  1. 获取全部留言

  2. 发表新留言

3. 服务器端代码

java 复制代码
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;
    }
}
java 复制代码
package com.example.demo;

import lombok.Data;

@Data
public class MessageInfo {
    public String from;
    public String to;
    public String say;
}

四. 图书管理系统

1. 准备工作

创建SpringBoot项目, 并引入SpringBoot依赖. 把前端页面的代码放到项目中.

  • 登录页面:
html 复制代码
<!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>
  • 图书列表:
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>

2. 约定前后端交互接口

需求分析

登录: 用户输入账号和密码完成登录功能.

列表: 展示图书

接口定义

登录接口:

java 复制代码
[URL]
POST /user/login
[请求参数]
name=admin&password=admin
[响应]
true //账号密码验证成功
false//账号密码验证失败

列表:

java 复制代码
[URL]
POST /book/getList
[请求参数]
⽆
[响应]
返回图书列表
[
 {
 "id": 1,
 "bookName": "活着",
 "author": "余华",
 "count": 270,
 "price": 20,
 "publish": "北京⽂艺出版社",
 "status": 1,
 "statusCN": "可借阅"
 },
 ...

属性说明:

3. 服务器端代码

登录页面

java 复制代码
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;
    }
}

图书列表

创建图书:
java 复制代码
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;
}
返回图书列表:
java 复制代码
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;
    }
}

五. lombook 介绍

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

使用方法:

也可直接使用 @Data注解, 只不过 @Data注解 比较粗暴:

@Data =

@Getter+@Setter+@ToString+@NoArgsConstructor +@RequiredArgsConstructor

六. 应用分层

为了不使我们的代码看起来比较杂乱, 我们使用应用分层来对代码进行分层管理.

Spring项目常见的三层应用分层:

Controller (接口层) : 负责与外部做交互.

Service (逻辑层) : 负责做逻辑处理.

Mapper / Dao (持久层) : 负责从数据库拿数据 (相当于数据库的客户端).

还有最底层的数据库 (DB) : 使用MySQL或Oracle (不在三层分层中).

MVC和三层架构的关系:

软件设计原则: 高内聚+低耦合

相关推荐
忘忧人生4 分钟前
docker 部署 java 项目详解
java·docker·容器
null or notnull31 分钟前
idea对jar包内容进行反编译
java·ide·intellij-idea·jar
言午coding2 小时前
【性能优化专题系列】利用CompletableFuture优化多接口调用场景下的性能
java·性能优化
幸好我会魔法2 小时前
人格分裂(交互问答)-小白想懂Elasticsearch
大数据·spring boot·后端·elasticsearch·搜索引擎·全文检索
SomeB1oody2 小时前
【Rust自学】15.2. Deref trait Pt.1:什么是Deref、解引用运算符*与实现Deref trait
开发语言·后端·rust
缘友一世2 小时前
JAVA设计模式:依赖倒转原则(DIP)在Spring框架中的实践体现
java·spring·依赖倒置原则
何中应3 小时前
从管道符到Java编程
java·spring boot·后端
SummerGao.3 小时前
springboot 调用 c++生成的so库文件
java·c++·.so
组合缺一3 小时前
Solon Cloud Gateway 开发:Route 的过滤器与定制
java·后端·gateway·reactor·solon
SomeB1oody3 小时前
【Rust自学】15.4. Drop trait:告别手动清理,释放即安全
开发语言·后端·rust