首页 > 图灵资讯 > 技术篇>正文

前后端分离

2023-06-27 14:59:32

1.分析项目存在的问题y3-1

前后端分离_spring

2.前后端分离y3-22.1介绍

前端和后端分离开发是指专门的前端开发人员负责项目开发过程中前端代码的开发,后端代码由后端开发人员负责,分工明确,各司其职,提高开发效率,并行开发前端和后端代码,加快项目开发进度。目前,越来越多的公司采用前端和后端分离开发模式,已成为当前项目开发的主流开发模式。

前端和后端分离开发后,工程结构也会发生变化,即前端和后端代码不再混合在同一maven工程中,而是分为前端工程和后端工程。

前后端分离_接口文档_02

2.2开发流程

前后端分离_java_03

2.2.1举例说明y3-2

前后端分离_接口文档_04

2.3前端技术栈y3-2开发工具

●VisualStudioCode

●hbuilder

技术框架

●nodejs

●VUE

●ElementUI

●mock

●webpack

3.yapiy3-33.1介绍y3-3-3

YAPI是一个高效、易用、功能强大的API管理平台,旨在为开发、产品和测试人员提供更优雅的接口管理服务。它可以帮助开发人员轻松地创建、发布和维护API。YAPI还为用户提供了良好的互动体验。开发人员只需使用平台提供的接口数据来编写

接口管理可以通过进入工具和简单的点击操作来实现。

YApi使接口开发更加简单高效,使接口管理更加可读、可维护,使团队合作更加合理。

源码地址:https://github.com/YMFE/yapi

要使用YAPi,需要自己部署。

前后端分离_接口文档_05

3.2使用y3-3

具体操作的部署不演示,这不是重点,可以理解

前后端分离_spring_06

4.Swagery3-44.1介绍

使用Swager,您只需要根据其规范定义接口和接口相关信息,然后通过Swager衍生出的一系列项目和工具生成各种格式的接口文档和在线接口调试页面。

官网:https://swagger.io/

knife4j是JavaMVC框架集成Swager生成Api文档的增强解决方案。

前后端分离_Swagger_07

<dependency>    <groupId>com.github.xiaoymin</groupId>    <artifactId>knife4j-spring-boot-starter</artifactId>    <version>3.0.2</version></dependency>

4.2使用方法y3-4

操作步骤:

1、导入knife4j的maven坐标

2、导入knife4j相关配置类别

3、设置静态资源,否则接口文档页面无法访问

4、在LogincheckFilter中设置不需要处理的请求路径

在master创建新的分支v1.2

前后端分离_spring_08

4.2.导入knife4j的maven坐标pomm.xml

<dependency>    <groupId>com.github.xiaoymin</groupId>    <artifactId>knife4j-spring-boot-starter</artifactId>    <version>3.0.2</version></dependency>

4.2.导入knife4j相关配置类别

前后端分离_接口文档_09

我们已经有了这个类别,只需要添加相关的注释

前后端分离_spring_10

WebMvcConfig

@Bean    public Docket createRestApi() {        // 文档类型        return new Docket(DocumentationType.SWAGGER_2)                .apiInfo(apiInfo())                .select()                .apis(RequestHandlerSelectors.basePackage("com.itheima.reggie.controller"))                .paths(PathSelectors.any())                .build();    }    private ApiInfo apiInfo() {        return new ApiInfoBuilder()                .title(瑞吉外卖)                .version("1.0")                .description(瑞吉外卖接口文件)                .build();    }

4.2.3设置静态资源,否则接口文档页面无法访问y3-4

前后端分离_Swagger_11

WebMvcConfig

@Override    protected void addResourceHandlers(ResourceHandlerRegistry registry) {        log.info(”静态资源映射开始...");        //设置静态资源,否则,接口文档页面将无法访问  y3-4        registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");        registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");        registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");    }

4.2.4在LogincheckFilter中设置不需要处理的请求路径y3-4

前后端分离_Swagger_12'

LoginCheckFilter

前后端分离_Swagger_13

测试生成接口文档

浏览器输入http://localhost:8080/doc.html

成功

前后端分离_java_14

导出文档

前后端分离_java_15

4.3常用注释y3-5-5

前后端分离_接口文档_16

以setmeal和R和setmealcontrler为例

Setmeal

package com.itheima.reggie.entity;import com.baomidou.mybatisplus.annotation.FieldFill;import com.baomidou.mybatisplus.annotation.IdType;import com.baomidou.mybatisplus.annotation.TableField;import com.baomidou.mybatisplus.annotation.TableId;import io.swagger.annotations.ApiModel;import io.swagger.annotations.ApiModelProperty;import lombok.Data;import java.io.Serializable;import java.math.BigDecimal;import java.time.LocalDateTime;/** * 套餐管理  3-8 */@Data@ApiModel(“套餐”)    ///加入实体书名注释,目的是让我们生成更清晰的界面文档  3-5publiclicy class Setmeal implements Serializable {    private static final long serialVersionUID = 1L;    @ApiModelProperty(主键)    private Long id;    //分类id    @ApiModelProperty(“分类id”)    private Long categoryId;    ///套餐名称    @ApiModelProperty(“套餐名”)    private String name;    ///套餐价格    @ApiModelProperty(“套餐价”)    private BigDecimal price;    //状态 0:停用 1:启用    @ApiModelProperty(“状态”)    private Integer status;    //编码    @ApiModelProperty(“套餐号”)    private String code;    //描述信息    @ApiModelProperty(描述信息)    private String description;    //图片    @ApiModelProperty(图片)    private String image;    @TableField(fill = FieldFill.INSERT)    private LocalDateTime createTime;    @TableField(fill = FieldFill.INSERT_UPDATE)    private LocalDateTime updateTime;    @TableField(fill = FieldFill.INSERT)    private Long createUser;    @TableField(fill = FieldFill.INSERT_UPDATE)    private Long updateUser;    //是否删除    private Integer isDeleted;}

R

package com.itheima.reggie.common;import io.swagger.annotations.ApiModel;import io.swagger.annotations.ApiModelProperty;import lombok.Data;import java.io.Serializable;import java.util.HashMap;import java.util.Map;//通用返回结果 ,服务器的响应数据最终将被包装成这个对象 8@Data@ApiModel(“返回结果”)public class R<T> implements Serializable { //继承Serializable接口  y1-14    @ApiModelProperty(“编码”)    private Integer code; /编码:1成功,0和其他数字是失败的    @ApiModelProperty(“错误信息”)    private String msg; //错误信息    @ApiModelProperty(数据”)    private T data; //数据    @ApiModelProperty(动态数据)    private Map map = new HashMap(); ///动态数据    public static <T> R<T> success(T object) {        R<T> r = new R<T>();        r.data = object;        r.code = 1;        return r;    }    public static <T> R<T> error(String msg) {        R r = new R();        r.msg = msg;        r.code = 0;        return r;    }    public R<T> add(String key, Object value) {        this.map.put(key, value);        return this;    }}

SetmealController

前后端分离_spring_17'

SetmealController

package com.itheima.reggie.controller;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;import com.itheima.reggie.common.R;import com.itheima.reggie.dto.SetmealDto;import com.itheima.reggie.entity.Category;import com.itheima.reggie.entity.Setmeal;import com.itheima.reggie.service.CategoryService;import com.itheima.reggie.service.SetmealDishService;import com.itheima.reggie.service.SetmealService;import io.swagger.annotations.Api;import io.swagger.annotations.ApiImplicitParam;import io.swagger.annotations.ApiImplicitParams;import io.swagger.annotations.ApiOperation;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.BeanUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cache.annotation.CacheEvict;import org.springframework.cache.annotation.Cacheable;import org.springframework.web.bind.annotation.*;import java.util.List;import java.util.stream.Collectors;///套餐管理控制层  5-3@RestController@RequestMapping("/setmeal@Slf4j@Api(tags = “套餐相关接口”)  //y3-5publiclicicicic class SetmealController {    @Autowired    private SetmealService setmealService;    @Autowired    private SetmealDishService setmealDishService;    ///方便我们在查询套餐分页时使用  5-9    @Autowired    private CategoryService categoryService;    /*///新包装  5-5  5-6    @PostMapping    public R<String> save(@RequestBody SetmealDto setmealDto){        log.info(包装信息 {}",setmealDto);        setmealService.saveWithDish(setmealDto);        return R.success(“新套餐成功”);    }*/    ///新包装  优化  y1-14    @CacheEvict(value = "setmealCache",allEntries = true)    @PostMapping    @ApiOperation(value = “新套餐接口”)    public R<String> save(@RequestBody SetmealDto setmealDto){        log.info(包装信息 {}",setmealDto);        setmealService.saveWithDish(setmealDto);        return R.success(“新套餐成功”);    }    ///套餐信息分页查询  5-9    @GetMapping("/page")    @ApiOperation(value = “套餐分页查询接口”  //y3-5    @ApiImplicitParams({            @ApiImplicitParam(name = "page",value = "页码",required = true),            @ApiImplicitParam(name = "pageSize",value = “每页记录数”,required = true),            @ApiImplicitParam(name = "name",value = “套餐名”,required = false)    })    public R<Page> page(int page,int pageSize,String name){        //分页结构器        Page<Setmeal> pageInfo = new Page<>();        Page<SetmealDto> setmealDtoPage = new Page<>();        //添加查询条件        LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper<>();        //添加查询条件 like模糊查询根据名称进行        queryWrapper.like(name!=null,Setmeal::getName,name);        //添加排序条件,根据更新时间的顺序排序        queryWrapper.orderByDesc(Setmeal::getUpdateTime);        //执行分页查询        ///这个句子将包装我们查询到的数据 所有包装在Page的对象pageInfo        setmealService.page(pageInfo,queryWrapper);        ///这个时候这种情况和菜品分页查询的原因是一样的。我们这里的套餐分类名称无法显示,因为        // 我们传输id,前端需要name 解决方案与菜肴的分页查询相同        //复制pageinfo给setmealDtopage,但不复制records,因为pageinfo对应的泛型是setmeal        //我们需要的是SetmealDto        ///这个副本只是副本page对象的普通属性,不包括records属性集合        BeanUtils.copyProperties(pageInfo,setmealDtoPage,"records");        List<Setmeal> records = pageInfo.getRecords();        List<SetmealDto> list = records.stream().map((item)->{            ///这个setmealdto是我们新new出来的,里面的属性是空的,我们需要赋值            SetmealDto setmealDto = new SetmealDto();            ///将Setmeal的普通属性复制到SetmealDto            BeanUtils.copyProperties(item,setmealDto);            ///获得套餐分类idid            Long categoryId = item.getCategoryId();            ////根据套餐分类id查询分类对象            Category category = categoryService.getById(categoryId);            if(category!=null){                ////获得套餐分类的名称                String categoryName = category.getName();                ///将分类名赋值给setmealDto对象                setmealDto.setCategoryName(categoryName);            }            return setmealDto;        }).collect(Collectors.toList());///收集这些setmealDto对象并集合        ///经过以上操作,我们得到了它 套餐分类名称 的 一般来说,setmealDto是setmel 的集合        setmealDtoPage.setRecords(list);///给setmealdtopagerecords赋值        return R.success(setmealDtoPage);    }    /**     * 删除套餐     * @param ids     * @return     */   /* @DeleteMapping    public R<String> delete(@RequestParam List<Long> ids){        log.info("ids:{}",ids);        setmealService.removeWithDish(ids);        return R.success(“成功删除套餐数据”);    }*/    /**     * 删除套餐  优化  y1-14     * @param ids     * @return     */    //allEntries = true 表示删除setmealCache下的所有缓存数据    @CacheEvict(value = "setmealCache",allEntries = true)    @DeleteMapping    @ApiOperation(value = “套餐删除接口”)    public R<String> delete(@RequestParam List<Long> ids){        log.info("ids:{}",ids);        setmealService.removeWithDish(ids);        return R.success(“成功删除套餐数据”);    }    /**     * 根据条件查询套餐数据  6-6     * @param setmeal     * @return     */    /*@GetMapping("/list")    public R<List<Setmeal>> list(Setmeal setmeal){        LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper<>();        queryWrapper.eq(setmeal.getCategoryId() != null,Setmeal::getCategoryId,setmeal.getCategoryId());        queryWrapper.eq(setmeal.getStatus() != null,Setmeal::getStatus,setmeal.getStatus());        queryWrapper.orderByDesc(Setmeal::getUpdateTime);        List<Setmeal> list = setmealService.list(queryWrapper);        return R.success(list);    }*/    /**     * 根据条件查询套餐数据优化  y1-14     * @param setmeal     * @return     */    @Cacheable(value = "setmealCache",key = "#setmeal.categoryId+'_'+#setmeal.status")    @GetMapping("/list")    @ApiOperation(value = “套餐条件查询接口”)    public R<List<Setmeal>> list(Setmeal setmeal){        LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper<>();        queryWrapper.eq(setmeal.getCategoryId() != null,Setmeal::getCategoryId,setmeal.getCategoryId());        queryWrapper.eq(setmeal.getStatus() != null,Setmeal::getStatus,setmeal.getStatus());        queryWrapper.orderByDesc(Setmeal::getUpdateTime);        List<Setmeal> list = setmealService.list(queryWrapper);        return R.success(list);    }}

在浏览器中输入测试http://localhost:8080/doc.html

成功,有了中文解释,可读性更强

前后端分离_spring_18

前后端分离_Swagger_19

前后端分离_spring_20

5.项目部署y3-65.1部署框架y3-6-6

前后端分离_spring_21

5.2部署环境y3-6-6

服务器:

●192.168.163.100(服务器A)

Nginx:部署前端项目,配置反向代理

Mysql:复制结构中的主库

●192.168.163.102(服务器B)

jdk:运行ava项目

git:版本控制工具

maven:工程建设工具

jar:基于内置Tomcat的springBoot项目打造jar包

Mysql:从库中主要从复制结构

●192.168.163.100(服务器A)

Redis:缓存中间件

5.3部署前端资源y3-7

上传前端打包的dist目录E:\java\lingsanziliao

前后端分离_java_22

前后端分离_接口文档_23

配置nginx

前后端分离_Swagger_24

前后端分离_Swagger_25

前后端分离_java_26

前后端分离_java_27

刷新

前后端分离_Knife4j_28

复制一个标签,启动nginx

前后端分离_Knife4j_29

访问浏览器输入http:/192.168.163.

访问成功

前后端分离_spring_30

点击登录查看浏览器发送请求y3-7

前后端分离_Swagger_31

5.4部署后端项目y3-8

接口文档_32

新建一哥javappp

前后端分离_Swagger_33

前后端分离_接口文档_34

将项目克隆到javapp中

前后端分离_java_35

脚本reggiestartart.sh

#!/bin/shecho =================================echo  自动部署脚本启动echo =================================echo 停止原运行中的工程APP_NAME=reggie_take_out11tpid=`ps -ef|grep $APP_NAME|grep -v grep|grep -v kill|awk '{print $2}'`if [ ${tpid} ]; then    echo 'Stop Process...'    kill -15 $tpidfisleep 2tpid=`ps -ef|grep $APP_NAME|grep -v grep|grep -v kill|awk '{print $2}'`if [ ${tpid} ]; then    echo 'Kill Process!'    kill -9 $tpidelse    echo 'Stop Success!'fiecho 准备从Git仓库提取最新代码cd /usr/local/javaapp/reggie_take_out11echohohout 最新代码gitt开始从Git仓库提取 pullecho 代码拉取完成echo 开始打包mvn clean package -Dmaven.test.skip=true > output.logcd /usr/local/javaapp/reggie_take_out11/targetecho nohup启动项目 java -jar reggie_take_out11-1.0-SNAPSHOT.jar &> reggie_take_out11.log &echo 项目启动完成

脚本在E:\java\lingsanziliao

上传脚本regiestartart.sh到javaapp

前后端分离_spring_36

前后端分离_Knife4j_37

修改权限

前后端分离_java_38

执行脚本

前后端分离_Knife4j_39

查看java运行过程,说明没有问题

前后端分离_Knife4j_40'

访问http:/192.168.163.

成功!!!

前后端分离_spring_41

但另一个问题是,我们的图片资源无法显示

前后端分离_spring_42

查看以下操作日志

前后端分离_接口文档_43

前后端分离_Swagger_44

很明显,在寻找图片资源时处理问题,因为这条路径是windows路径,在java文件中也可以看到

因此,我们需要修改指定的新路径

前后端分离_Swagger_45

前后端分离_spring_46

修改图片资源路径

前后端分离_Swagger_47

在img目录中创建图片

前后端分离_Knife4j_48

上传图片

前后端分离_接口文档_49

再次执行脚本

前后端分离_Knife4j_50'

访问http:/192.168.163.

成功

前后端分离_Knife4j_51

到目前为止,项目已经完成

上一篇 高温预警:请问大家清楚二叉树、二叉查找树、二叉排序树、二叉平衡树的区别是什么吗?
下一篇 如何实现递归的

文章素材均来源于网络,如有侵权,请联系管理员删除。