目录

前言

约定前后端交互接口

计算器

        需求分析

        接口定义

        前端代码

        后端代码

       启动程序

用户登录

        需求定义

        约定前后端接口

                需求分析

                接口定义

                        校验接口

                        查询登录用户接口

        后端代码

        前端代码

留言板

        需求

        约定前后端交互接口

                需求分析

                接口定义

                        存储留言信息

                        获取全部留言

        后端代码实现

        前端代码实现

        测试结果

Lombok库

        Lombok常用注解

        引入依赖

        插件添加Lombok依赖

        原理


前言

在上一篇中,我们讲解了什么是分层架构、三层架构,那么本篇,我们就使用三层架构来对案例进行分层。

案例的学习目的主要在于:

  1. 理解前后端交互过程;
  2. 接口传参,数据返回,以及页面展示。

约定前后端交互接口

约定好“前后端交互接口”是Web开发中的关键环节

接口又称为API(Application Programming Interface),我们一般讲到接口或者API,都指的是同一个东西。是指用应用程序对外提供的服务的描述。

简单来说,就是允许客户端发佛那个哪些HTTP请求给服务端,并且每种请求预期获取什么样的响应

而对于现在“前后端分离”模式,前端和后端通常都是由不同团队进行开发,那么双方在开发之前,就会约定好交互的方式,客户端发起请求,服务器提供对应的服务。服务器提供的服务种类也有很多,客户端按照双方约定指定选择哪一种服务

所以,在开发的时候,一般就会将约定的内容写在文档上,就是"接口文档",可以理解为程序的操作说明书。

那么接口文档是由哪一方提供的?

接口文档通常是由服务端来写,交给服务端来使用,一旦写好,尽量不要轻易改变。


计算器

需求分析

我们要实现一个加法计算器功能,对两个整数进行相加,需要客户端提供参与计算的两个数,服务端返回计算的结果。

接口定义

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

请求参数

响应数据

  • Content-type:text/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>

 后端代码

对于这个简单的计算器,就不分层了。

package com.example.demo.Controller;


import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/calc")
public class CalcController {

    @RequestMapping(value = "/sum",produces = "text/html")
    public String sum(Integer num1,Integer num2){
        return "<h1>"+num1+" + "+num2+"的结果为:"+(num1+num2)+"</h1>";
    }

}

启动程序

我们把后端启动后,就通过“http:127.0.0.1:8080/calc.html”进行访问。

用户登录

需求定义

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

  1. 如果不正确,前端进行告知;
  2. 如果正确,就跳转到首页,首页显示当前登录用户的账号;
  3. 后序再访问首页,可以获取到登录用户信息。

约定前后端接口

需求分析

后端人员,只需要提供两个功能:

  • 登录界面:通过前端提供的账号和密码,进行校验账号和密码是否正确,并返回响应
  • 首页:告知前端当前登录用户账号,如果当前已经有用户登录,返回登录账号,如果没有,则返回空。

接口定义

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

请求参数

响应数据

Content-Type:text/html

响应内容:true/false 

查询登录用户接口
  • 请求路径:/user/getLoginUser
  • 请求方式:GET
  • 描述:查询当前登录的用户

无请求参数

响应数据

Content-Type:text/html

响应内容:当前登录的账号

后端代码

package com.example.demo.Controller;


import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

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

    /**
     * 处理用户登录请求
     *
     * @param username 用户名,用于识别用户身份
     * @param password 密码,用于验证用户身份
     * @param session HTTP会话,用于存储用户登录状态
     * @return 登录成功返回true,否则返回false
     */
    @RequestMapping(value = "/login",method = RequestMethod.POST)
    public boolean login(String username, String password, HttpSession session){
        // 检查用户名和密码是否为空,如果任一为空,则登录失败
        if(!StringUtils.hasLength(username)||!StringUtils.hasLength(password)){
            return false;
        }
        // 验证用户名和密码是否匹配预设的管理员凭证
        if("admin".equals(username)&&"123".equals(password)){
            // 登录成功,将用户名存储在会话中,以维持登录状态
            session.setAttribute("username",username);
            return true;
        }
        // 默认登录失败
        return false;
    }


    /**
     * 请求映射到获取当前登录用户的用户名
     *
     * 此方法通过HttpServletRequest对象获取当前会话,并尝试从会话中获取用户名
     * 如果会话中存在用户名,则返回该用户名;否则返回空字符串
     *
     * @param request HttpServletRequest对象,用于获取当前会话
     * @return 当前登录用户的用户名,如果未找到则返回空字符串
     */
    @RequestMapping("/getLoginUser")
    public String getLoginUser(HttpServletRequest request){
        // 获取当前请求的会话
        HttpSession session=request.getSession();
        // 检查会话是否非空
        if(session!=null){
            // 从会话中获取用户名
            String username=(String)session.getAttribute("username");
            // 返回获取到的用户名
            return username;
        }
        // 如果会话中没有用户名,返回空字符串
        return "";
    }


}

前端代码

login.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="js/jquery-3.7.1.min.js"></script>
  <script>
    function login() {
      $.ajax({
        type:"POST",
        url:"/user/login",
        data:{
          username:$('#userName').val(),
          password:$('#password').val()
        },
        success:function(result){
          if(result){
            //跳转到首页
            alert("登录成功!");
            location.href="/index.html";
          }else{
            alert("账号或密码有误");
          }
        }
      });
    }

  </script>
</body>

</html>

index.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({
            type:"get",
            url:"/user/getLoginUser",
            success:function(result){
                $("#loginUser").text(result);
            }
        });
    </script>
</body>

</html>

测试结果: 

留言板

需求

  • 输入留言信息,点击提交,后端把数据存储起来
  • 页面展示输入留言的信息

约定前后端交互接口

需求分析

后端需要提供两个服务

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

接口定义

存储留言信息

接口路径:/message/publish

请求方式:POST

描述:存储前端发来的数据

响应:JSON格式

        ok:1

获取全部留言

在后端中,我们用List来表示,用JSON可以描述这个数据。

请求路径:/message/getList

请求方式:GET

响应JSON格式数据。

后端代码实现

Model包:MessageInfo实体类

package com.example.demo.Model;


import lombok.Data;

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

Controller包下:MesgController类

package com.example.demo.Controller;

import com.example.demo.Model.MessageInfo;
import com.example.demo.Service.MesgService;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * 消息控制器类,用于处理与消息相关的HTTP请求
 */
@RestController
@RequestMapping("/message")
public class MesgController {

    /**
     * 获取消息列表
     * 
     * @return 消息信息列表
     */
    @RequestMapping("/getList")
    public List<MessageInfo> getList(){
        MesgService mesgService=new MesgService();
        return mesgService.getList();
    }

    /**
     * 发布新消息
     * 
     * @param messageInfo 新消息的信息,以JSON格式传入
     * @return 消息发布结果的字符串表示
     */
    @RequestMapping(value = "/publish",produces = "application/json")
    public String publish(@RequestBody MessageInfo messageInfo){
        System.out.println(messageInfo.toString());
        MesgService mesgService=new MesgService();
        return mesgService.addMessage(messageInfo);
    }
}

Service包下:MesgService类

package com.example.demo.Service;

import com.example.demo.Dao.MesgDao;
import com.example.demo.Model.MessageInfo;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.List;

/**
 * 提供留言相关服务的类
 */
public class MesgService {

    /**
     * 获取留言列表
     * 
     * @return 留言信息列表
     */
    @RequestMapping("/getList")
    public List<MessageInfo> getList(){
        // 创建MesgDao实例来访问数据库
        MesgDao mesgDao=new MesgDao();
        // 调用Dao方法获取留言列表并返回
        return mesgDao.getList();
    }

    /**
     * 添加新的留言
     * 
     * @param messageInfo 留言信息对象,包含留言的详细信息
     * @return 添加成功返回"{\"ok\":1}",否则返回"{\"ok\":0}"
     */
    //存储留言
    public String addMessage(MessageInfo messageInfo){
        // 检查留言信息是否完整
        if(!StringUtils.hasLength(messageInfo.getFrom())||
                !StringUtils.hasLength(messageInfo.getTo())||
                !StringUtils.hasLength(messageInfo.getSay())){
            // 如果留言信息不完整,返回失败信息
            return "{\"ok\":0}";
        }
        // 创建MesgDao实例来访问数据库
        MesgDao mesgDao=new MesgDao();
        // 调用Dao方法添加留言
        mesgDao.addMessage(messageInfo);
        // 留言添加成功,返回成功信息
        return "{\"ok\":1}";
    }
}

 数据访问层:MesgDao类

package com.example.demo.Dao;

import com.example.demo.Model.MessageInfo;
import java.util.ArrayList;
import java.util.List;

/**
 * MesgDao类负责处理消息信息的持久层操作
 * 它提供了获取消息列表和添加新消息的功能
 */
public class MesgDao {
    // 存储消息信息的列表
     static List<MessageInfo> list = new ArrayList<>();

    /**
     * 获取消息信息列表
     * 如果列表为空,将创建并返回包含两个示例消息的列表
     *
     * @return 包含MessageInfo对象的列表
     */
    public List<MessageInfo> getList() {
        return list;
    }

    /**
     * 向消息列表中添加新的消息信息
     *
     * @param messageInfo 要添加的消息信息对象
     */
    public void addMessage(MessageInfo messageInfo) {
        list.add(messageInfo);
    }
}

前端代码实现

<!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 id="message-list"></div>
    </div>

    <script src="js/jquery-3.7.1.min.js"></script>
    <script>
        clearMessages();
        function clearMessages() {
            // 清空留言列表
            $("#message-list").empty();
        }
        $.ajax({
            type: "get",//请求类型
            url: "/message/getList",//后端路径
            success: function (result) {
                for (var message of result) {
                    var divE = "<div>" + message.from + "对" + message.to + "说:" + message.say + "</div>";
                    $("#message-list").append(divE);
                }
            }
        });


        function submit() {
            //1. 获取留言的内容
            var from = $('#from').val();
            var to = $('#to').val();
            var say = $('#say').val();
            if (from == '' || to == '' || say == '') {
                alert("请输入完整!");
                return;
            }
            

            $.ajax({
                type: "post",
                url: "/message/publish",
                contentType: "application/json",
                data: JSON.stringify({
                    "from": from,
                    "to": to,
                    "say": say
                }),
                success: function (result) {
                    //
                    if (result != null) {
                        var divE = "<div>" + from + "对" + to + "说:" + say + "</div>";
                        //将节点添加在页面上
                        $("#message-list").append(divE);
                        //清空输入框
                        $("#from").val("");
                        $("#to").val("");
                        $("#say").val("");
                    }
                    else {
                        alert("留言失败!");
                    }
                }
            });
        }

    </script>
</body>

</html>

测试结果


在上面MessageInfo实体类中,我们用到了一个注解@Data,这个注解有什么用?而且我们把类中的属性都设为私有,没有提供get和set方法,其他类是如何拿到类中属性的?

其实这都是跟@Data注解有关。可以看到,这个注解是在Lombok库中的。

 

Lombok库

Lombok 是一个非常流行的 Java 库,它通过注解的方式简化了 Java 的样板代码(boilerplate code),从而使代码更加简洁、易读和易维护。Lombok 主要通过注解处理器(annotation processor)在编译时自动生成代码,而不需要开发者手动编写。

Lombok常用注解

除了图中这几个,还有上面的@Data。

@Data@Getter, @Setter, @ToString, @EqualsAndHashCode, @NoArgsConstructor 的组合注解,非常适合用于实体类。

引入依赖

        <dependency>
    		<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>

除了上面这种,我们还可以在创建spring项目的时候将Lombok勾选起来即可。

插件添加Lombok依赖

我们需要在插件中下载Edit Starters。 

安装好后,我们在项目栏中找到pom.xml文件。

 

通过这个插件,我们能更简便的导入依赖。 

原理

Lombok 的核心原理是通过注解处理器在编译阶段动态插入代码,并利用字节码操作库确保生成的代码与手动编写的代码在运行时行为一致。这种方式不仅减少了样板代码的编写,还避免了运行时的额外开销。

Lombook利用了java编译器的注解处理机制,在编译阶段扫描代码中的注解,并根据注解生成相应的代码。

我们观察经过编译阶段后的字节码(.class)文件,其中已经是没有注解了,反而出现了我们所需的其他get和set方法等。

 可以看出:

lombok是⼀款在编译期⽣成代码的⼯具包.

普通Java程序运行原理:

 Lombok作用如下:

 Lombok优点:

  1. 减少样板代码,提高开发效率。
  2. 提高代码可读性,专注于业务逻辑。

  3. 减少出错概率,生成的代码经过严格测试。

  4. 提供强大的功能扩展,如构建器模式、日志对象等。

  5. 与主流 IDE 集成,提供无缝的开发体验。

  6. 不引入运行时依赖,不影响性能。


以上就是本篇所有内容~

若有不足,欢迎指正~ 

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐