大家好,这次我们来讲解前后端分离的原理,前后端是通过什么分离的,分离的实质是什么

        这次文章是基于我最近做的项目的总结,所以上面讲到的只是点都会在我的文章中体现

谢谢大家,仅供个人学习,不喜勿喷~

一、前后端分离的本质(思想)

前言:

  前后端分离他不是依赖某一个工具,不是依赖某一种技术,而是一种“思想”。即:后端只负责“数据相关”管理数据操作数据库,前端只负责“页面相关收集数据给后端,渲染页面给用户。这是前后端分离的第一核心:明确的 “职责边界”(谁该做什么,谁不该做什么)。

  其二核心是他们都遵循着一个通讯协议:“HTTP协议”。 这个协议让我们的前后端都能按照一定的“规则”交流,说个里面最核心的:前端请求路径与后端的接口路径保持一致可以用于实现:前端某个元素组件所绑定的事件函数可以练习后端来处理。 举个例子:前端设置了一个button按钮,这个按钮绑定了点击事件,事件内容是执行对应的js函数,与此同时你在后端写了一个方法想来处理前端的内容,那么你再后端写一段url路径,前端写响应的POST或者GET请求+对应后端的路径,那么这个js函数就会自动查找后端与其路径相同的方法来执行。

本次相关技术

2.接下来说一下我们项目用到的技术,这些技术可以方便我们实现前后端分离:

Ajax实现异步刷新发送请求

springboot提供了 “快速开发后端接口的环境”—— 比如 @RestController@PostMapping@RequestParam 这些注解,帮你快速定义 “接口路径、请求方式、参数规则

maven依赖管理

Mysql数据库、JDBC使用后端操作数据库、Druid连接池

二、项目讲解与步骤

  1.项目需求解析

我们看出来需求一共分为三个大点

1.订单以及订单相关信息的录入

2.订单的保存

3.查询不同状态的订单信息

针对这几个点我们依次来做分析:

首先是第一点:从用户手中得到信息,那么就是前端收集信息并且把信息传输给后端保存的环节,初步构想实现那就是:

1.利用input输入框让用户输入对应的信息,每个输入框有着特定的id然后在js里通过输入框id创建对象然后value给相应的对象赋值。比如:

html

 <input type="text" id="userName" placeholder="请输入用户名"> <!-- 用户名输入框 -->

js(这里使用了jquary库,简化了js的操作)

userName: $("#userName").val(), // 拿到用户填的用户名(比如“张三”)

2.前端收集完信息后发送给后端:可以通过Ajax通过url路径以及POST/GET请求发送JSON格式的消息给后端

$.ajax({
  url: "/api/order/add", // 后端接口地址
  type: "POST",          // 请求方式
  contentType: "application/json", // 告诉后端:我传的是JSON
  data: JSON.stringify(orderData), // 把用户信息转成JSON字符串(比如 {"userName":"张三",...})
  success: function(res) { ... } // 接收后端响应
});

3.后端接收json格式数据,执行相应代码

第二点需求:订单信息的保存,要想让信息长久保存并且方便管理,那么使用MySQL数据库管理数据是最方便的,所以基于后端java我们可以使用jdbc来操作数据库,连接方式我们使用现在最主流的druid连接方式,所以我们的初步构想就是:

1.配置druid的properties配置文件,然后通过druid连接我们MySQL数据库

2.基于druid创建相应的connection对象来创建preparestament对象,然后通过preparestament对象来执行相应的sql语句(insert、select....)

第三点需求:查询不同状态下的订单,其实和需求二一样,都是jdbc技术使用后端操控数据库(select  where),然后把结果渲染给前端,这里不再过多赘述

说实话,刚刚分析了这么多,对应我们最本质的实际操作就是MySQL表格的“增删改查”

在实际实现上并没有什么难点,所以慢慢来~

2.正式开发流程

有了前面的需求解析,我们就知道要干什么了,我们知道我们的项目是基于MySQL管理数据并且druid连接方式的,并且我们还会使用springboot来方便我们管理操作接口

【环境配置】所以我们第一步就是对于环境的配置

  1.对数据库配置(建立数据库、连接druid池)

首先打开navicat,新建一个数据库takeway_order,然后新建查询来建立表格

建立好我们的表格后,我们需要后端Java操控我们的表格,因此我们来连接我们的MySQL(基于druid连接池连接)

所以我们打开idea,新建一个spring项目,然后再在resource文件夹配置我们的druid的properties

(建议先把所有需要用的依赖全部导入先Web、JDBC、MySQL 驱动、MyBatis、Druid)

配置druid文件

然后按照druid连接方式连接MySQL(我这里自动连接方式报错了,所以我新建了一个类直接手动连接)

2.初始化相应表的数据,前后端数据传输与数据库操作要使用

        新建一个Order类,里面初始化我们后面需要用到的数据

3.【抽象接口】针对需求写对应的标准接口以及具体的实现方法

        新建service软件包,里面建立相应规范的接口和具体实现的方法类

接口:

具体实现类:

注意看代码里面的逻辑是怎么样的

本质结构其实就是:基础的逻辑判断+sql语句的调用

4.【JDBC】补全具体实现类方法里面的sql操作

        细心的盆友应该注意到我在具体的方法实现类里面有着刚刚没有提及到的类OrderDao类,这个类是具体执行sql语句的,我后来想了想,我觉得应该是先规定方法规范:“我要做什么”,再去想实际的方法内容:“我要怎么做”。

        所以我们现在来补全sql语句的操作,即jdbc

1.添加订单操作

具体流程太熟悉了,其实就是jdbc的插入语句

简单来说就是创建connection对象,通过connection对象创建出preparestatement预编译对象,通过preparestatement对象执行sql的insert语句

具体的执行逻辑再来讲解以下吧(因为之前学jdbc的时候没有写过博客,所以现在来稍微补充以下),这里的insert是简写过后的,具体执行逻辑我现在来讲解:

插入的逻辑非常简单,sql的insert预编译语句后面的问号既是对应相应字段的参数,预编译对象(后面统称pstmt)调用set数据类型方法来给参数传值,set方法里面需要传入两个参数,

第一个参数是int类型,告诉系统你这个信息对应着上面哪一个问好的值

第二个参数就是你想要填入的值的内容

比如说pstmt.setString(2,“香飘飘”) 意思就是在第二个问号参数填入“香飘飘”这个值

2.查询表格内容

这个我觉得才是有的讲的东西,刚好巩固以下之前学jdbc的内容

前面都是一样的都是创建connection对象和pstmt对象,不多说了,重点在于它的输出,这里规范输出为特定对象集合list的形式,我们一起来看一下他是怎么实现的吧!

我们先从简单的例子看起,不看我们的项目先

比如:查询tb_brand这个表格的内容

        首先明确一点:执行查询语句会返回一个resultset的结果集,这个结果集是不方便看的,就是只有数据库能读懂这是什么,因此,我们需要把结果集转换为我们方便看,程序看得懂的东西——集合

        jdbc的创始人也注意到了这一点,因此resultset的对象也有相应的办法解决

        我们刚刚说到resultset集合我们读不懂,实际上是不方便查看,它的真正结构可以类似于“栈”和头指针,指针的初始位置是第0个元素的正上方。

所以,基于resultset的“栈”结构我们可以使用遍历的方式把他挨个读取,然后再放到集合的方式方便程序和我们读

        我们首先创建一个brand的list集合,待会我们brand对象数据就存放再此,接着我们使用循环来遍历resultset集合里面的内容

        调用resueltset对象方法的“next”,该方法会返回一个布尔值,这个方法的内容是把指针往下移动一格,如果有数据,则返回true,否则false。基于这种方式,我们就可以依次遍历resultset里面的各个元素

        get数据类型就是rs对象对数据库字段内容的读取,括号内的参数填字段名称

        读取完数据后就需要把数据封装到特定的集合中,这个时候使用类的setter方法把刚刚读取到的数据封装到特定对象中,最后使用集合的add方法把每一个封装好的对象装到集合中

//1.定义sql语句
        String sql1="select * from tb_brand";
        //2.声明preparestatement对象用以来执行sql语句
        PreparedStatement pstmt = connection.prepareStatement(sql1);
        //3.使用pstmt对象执行sql语句,并且把结果集封装到Resultset里面
        ResultSet rs = pstmt.executeQuery();


        //4.利用rs指针读取数据,把数据封装到Brand对象里面
        //先定义集合,集合里面装Brand对象
        List<Brand> brandList=new ArrayList<>();
        while (rs.next()){
            Brand brand=new Brand();//先创建brand对象,后面操作要把数据放入该对象
            //先用rs对象从Resultset里面读取数据
            int id=rs.getInt("id");
            String brand_name=rs.getString("brand_name");
            String company_name=rs.getString("company_name");
            int ordered=rs.getInt("ordered");
            int status=rs.getInt("status");
            String description=rs.getString("description");

            //读取完数据,接下来放置数据到每一个对象里面(类的setter方法)
            brand.setId(id);
            brand.setBrand_name(brand_name);
            brand.setCompany_name(company_name);
            brand.setOrdered(ordered);
            brand.setStatus(status);
            brand.setDescription(description);

            //对象被放入数据后,装入到集合里面
            brandList.add(brand);

        }
System.out.println(brandList);
        pstmt.close();
        connection.close();
        rs.close();

我们的项目也是同理,只不过我们把读取和封装对象的操作合并了

3.根据状态查询订单

        内容和前面类似,就是条件查询,然后再where条件增加参数,最后和查询一样把数据封装成对象,再把对象装到集合里面的操作,这里不过多赘述

4.更新订单状态

        sql语句变为了update修改语句,同样没有什么好说的

5.【springboot】后端“接口”的编写—接收前端请求

        基本方法逻辑我们都编写完了,但是我们要怎么知道前端的用户要求我们做什么呢?这个时候我们就需要后端接口来接收前端的HTTP请求,这样我们才能知道前端需要调用后端的某一个功能。 

        提示以下:这里说的“接口”并不是interface,而是针对前端业务的“业务后端接口”,它的本质是一个类

        首先,因为有着springboot,我们会有很多方便的注解,因此我们的开发得到了极大的便利

我们现在就来说一下每一个注解的用途是什么

        1.@RequestController注解:作用是标记了这个类是“后端业务接口类”返回的数据会自动变成前端所需要的json数据

        2.@RequestMapping注解:作用如同名字一样,他是一张“map”地图,告诉前端:“后端业务接口在哪里”,里面传的参数就是后端业务接口的路径,对应到每一个具体的方法就是每一个方法的路径是什么

        3.@Autowired注解:自动导入某一个类的对象,到时候如果要使用哪个类的方法的话,直接使用那个类的对象调用方法即可调用

        讲完了初步的注解,接下来我们来讲怎么具体接收前端的请求然后做出相应的处理

      

          1.新增订单:前端POST请求

        首先来看注解:@PostMapping,和前文差不多,这是针对post请求的路径地址,如果有前端想访问使用方法,那它的请求方式必须是POST,它的url必须是这个类的requestmapping+对应方法的mapping,比如说我想使用这个方法,那他的Ajax或者axio必须这么写:

        type:POST        (因为这个方法的注解规定了POST请求)

         url:/api/order/add (该方法对应的路径)

    

再来看第二个注解:@RequestBody:这个注解会自动识别前端请求体返回的json数据,并且把返回的数据转换成对象的数据,这样就方便后端操作

        然后我们再来看这个方法,返回类型要求是键值对返回类型:即(“键”,值)的类型,这种数据类型是方便springboot的@RestController转换为json类型返回给前端

 Q:为什么要返回给前端?A:因为只有前端拿到了对应的HTTP数据前端才知道自己的请求到底成功了没有,或者某一个逻辑是否符合预期,这样前端才能渲染出对应的界面给用户

        所以我们第一步就是现在方法里面创建好键值对集result,准备好返回数据给前端

接着直接调用具体方法,这个具体方法其实就是我们第三步里面讲过的规范方法的接口的具体实现类的方法——orderservice类的方法,该类里面的方法结构在前文也讲述过了:逻辑判断+调用sql语句的方法

        我们这里的需求是“新增订单”,对应的sql语句就是insert,jdbc在使用insert语句会返回受影响的行数,我们这里每次操作只会新增一条语句,因此我们可以根据insert的返回值是0或者1来判断insert语句是否执行成功,若逻辑错误(比如参数类型错误)即返回值为0,那么就会返回异常的请求状态码和请求数据,同理,若insert语句没问题就会返回正确的数据,最后这些数据都是被封装到result这个键值对里面的,由@RestController转换为json格式给前端

请求失败,前端就会手到json格式的错误信息

新增成功也就会收到相应的json数据

        2.查询所有订单:前端GET请求

注解在这里就不过多说了,和前文一样,是mapping注解,只不过请求方式变成了GET

然后和前文一样,都是需要返回键值对类型然后转换为json类型给前端

方法体也是一样的,只不过不同查询所有一个已经有的值怎么查都不不会出错,因此这里并没有像前文一样设置一个逻辑判断是否执行成功

前端收到返回的json数据

3.查询特定状态的订单:前端GET请求

方法体和mapping我这里就不多说,这里需要补充的一点就是@RequestParam这个注解,这个注解的作用就是可以从url读取参数值,然后把该参数转换为我们想要的数据类型

展示以下不同状态的参数0123

4.修改订单数据:前端POST请求

讲过的这里不再阐述,这个本质上就是调用update修改语句再加上where条件

这个我们等会结合前端来具体说明

6.【Ajax】前端数据的获取以及请求的发送

前端html+css的静态页面这里就不说了,无非就是盒子与表单的搭建,这个步骤我们重点讲解前端对于数据的获取以及给后端发送请求,再谈谈两者前后端的联系

虽然前面说不管静态页面了,但是部分代码以及页面外观我们还是得了解以下的

可以看到,正如我们需求解析所说,我们通过input标签来让用户输入数据,再通过“提交订单”这个按钮,通过键值对的形式把用户输入的数据封装到对象当中:

1.输入框input从用户哪里获取数据

<input type="text" class="form-control" id="userName" name="userName" required placeholder="请输入下单人姓名">

2.数据的封装

先通过button来告诉用户点击这里来提交

<button type="button" class="btn btn-primary" id="submitOrder">提交订单</button>

然后绑定点击事件(这里是jquary的简写形式)执行对应的封装函数

// 提交订单按钮点击事件
        $("#submitOrder").click(function() {
            submitOrder();
        });

对应的封装函数,把用户输入的内容通过键值对保存,最后封装到orderData对象里面

/**
     * 新增订单:Ajax POST请求后端接口
     */
    function submitOrder() {
        // 获取表单数据
        let orderData = {
            userName: $("#userName").val(),
            phone: $("#phone").val(),
            dishName: $("#dishName").val(),
            totalPrice: parseFloat($("#totalPrice").val()),
            orderStatus: parseInt($("#orderStatus").val())
        };

以上,我们就完成了从用户手中获取数据,接下来,我们就需要把数据发送给后端,并且发送请求,让后端帮我们处理着一些数据

基于刚刚对用户数据的获取,我们接下来就接着上文,来讲第一个功能:新增订单

        接上上文,使用“提交订单”这个按钮把数据封装到对象里面,我们接下来就要发送请求,请求后端对我们前端接收到的数据做处理

        首先,无论是Ajax还是axio,发送请求的格式都是差不多的,我们这里使用的Ajax,那这里就先展示以下Ajax的请求格式

//发送Ajax请求

         $.ajax({                   //基于jquery的ajax方法请求格式
             url: "接口地址",
             type: "请求方式",
             // 其他配置...
             success: function(响应数据) {
                 // 请求成功后执行的代码
             },
             error: function(错误信息) {
                 // 请求失败后执行的代码
             }
         });

所以可以看到,我们必不可少的就是url请求地址、请求方式、成功回调函数、失败回调函数

后面的contentType是告诉后端请求体是json格式

data.json.stringfy(orderData)这里是把我们刚刚封装到对象的表单数据转换为后端需要的json数据

至此,这是我们程序运行到以上代码,从用户前端获取到了数据,用把数据打包好再发送给后端,接下来讲以下成功回调与失败回调函数

先来看success:function(res)这里,这个括号内传入的参数是什么

这个res其实就是我们前文后端业务“接口”每个方法都会返回的result

那你可能就有疑问:程序是怎么知道这个就是后端发送的result呢?

答:正是因为他是“成功回调”函数,请求发送成功那么后端就会返回值,后端返回的值是唯一的,那么这个res不就是后端唯一返回的result吗

我们再来看看官方的回答:

所以可以看到逻辑,当后端逻辑正确就会发送状态码200,当前端收到相应的状态码,就会逻辑判断,然后执行相应的事件,渲染相应的页面给用户看

整体展示

效果:新增成功

效果:新增失败

所以同理,查询订单操作思路也是一样的

订单查询

按照状态查询订单

也就多了一个拥有键值对的对象,上文说的@RequestParam注解相联系,由于是GET请求,参数会放在url里面,因此这个RequestParam注解就可以读取到url里面的参数将其利用

4.更新订单状态

这里和上文同理,都是把参数放在了url里面,到时候使用@Requestparam注解即可

最后是渲染订单的函数

三、项目效果演示

新增订单

用户点击提交订单

浏览器会给出提示“新增成功”

并且在下面渲染已有的订单,用户可以看到刚刚增加的订单以及过往的订单

在数据库也能同步看到表格更新

查询订单

用户点击查询订单即可查询现有所有订单

用户也可查询不同状态的订单

修改订单状态

用户点击“操作”类型按钮即可修改订单状态,修改成功会有提示

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐