SpringBoot前后端分离开发拆分和部署
1、前后端分离介绍
以前的项目开发都是将前端代码和后端代码混合在一个java项目中,这样是不利于管理和项目进度的
前后端分离开发,就是在项目开发过程中,对于前端代码的开发由专门的前端开发人员负责,后端代码则由后端开发
人员负责,这样可以做到分工明确、各司其职,提高开发效率,前后端代码并行开发,可以加快项目开发进度。
目前,前后端分离开发方式已经被越来越多的公司所采用,成为当前项目开发的主流开发方式。
前后端分离开发后,从工程结构上也会发生变化,即前后端代码不再混合在同一个maven工程中,而是分为前端工程和后端工程。
前端项目一般会打包部署到【nginx服务器上】
后端代码一般会打包部署到【Tomcat服务器上】
前端通过指定的接口来进行访问后端就可以实现数据交换,但是前后端分离项目最重要的一点就是需要【配置跨域】,否则前后端会请求不到
2、开发流程
接口(API接口):就是一个请求的http地址,主要是定义:请求路径、请求参数、请求方式、响应数据等内容
3、前端技术栈
开发工具:
1、Visual Studio Code
2、hbuilder
技术框架
1、node.js
2、vue
3、ElementUI
4、mock:测试模拟数据
5、webpack:打包工具
4、YAPI定义接口
YApi是高效、易用、功能强大的api管理平台,旨在为开发、产品、测试人员提供更优雅的接口管理服务。可以帮助
开发者轻松创建、发布、维护API,YApⅰ还为用户提供了优秀的交互体验,开发人员只需利用平台提供的接口数据写
入工具以及简单的点击操作就可以实现接口的管理。
YApi让接口开发更简单高效,让接口的管理更具可读性、可维护性,让团队协作更合理。
官网地址:https://hellosean1025.github.io/yapi/
需要使用yapi需要自己部署
YAPL安装和部署
自己百度,后面我整理一遍也会发出
5、Swagger
使用Swagger你只需要按照它的规范去定义接口及接口相关的信息,再通过Swagger衍生出来的一系列项目和工具,
就可以做到生成各种格式的接口文档,以及在线接口调试页面等等。
官网地址:https://swagger.io/
knife4j是为ava MVC框架集成Swagger生成Api文档的增强解决方案。
<!-- https://mvnrepository.com/artifact/com.github.xiaoymin/knife4j-spring-boot-starter -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
5.1、使用步骤
1、导入坐标
2、导入相关配置
@Slf4j
@Configuration
@EnableSwagger2
@EnableKnife4j
public class WebMvcConfig extends WebMvcConfigurationSupport {
@Bean
public Docket createRestApi() {
// 定义文档的类型
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
// 扫描控制器生成接口
.apis(RequestHandlerSelectors.basePackage("com.zcl.reggie.controller"))
.build();
}
/**
* 描述接口文档
* @return
*/
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("瑞吉外卖").version("1.0").description("瑞吉外卖接口文档").build();
}
}
3、设置静态资源。否则接口文档页面无法访问
改接口生成页面不是我们自己写的是由筷架自动生成的,需要在上面的类中添加该方法
/**
* 设置静态资源映射放行
* @param registry
*/
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
log.info("开始进行静态支援映射...");
registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
4、在LonginCheckFilter中设置不需要处理请求路径
启动项目访问
6、Swagger常用注解
注解 | 说明 |
---|---|
@Api | 用在请求的类上,例如Controller,表示对类的说明 |
@ApiModel | 用在类上,通常是实体类,表示一个返回值响应数据的信息 |
@ApiModelProperty | 用在属性上,描述响应类的属性 |
@ApiOperation | 用在请求的方法上,说明方法的用途、作用 |
@ApilmplicitParams | 用在请求的方法上,表示一组参数说明 |
@ApilmplicitParam | 用在@ApilmplicitParams注解中,指定一个请求参数的各个方面 |
使用在类或属性上的注解示例
/**
* 套餐
*/
@Data
@ApiModel("套餐")
public 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;
@ApiModelProperty("创建时间")
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@ApiModelProperty("修改时间")
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@ApiModelProperty("创建人")
@TableField(fill = FieldFill.INSERT)
private Long createUser;
@ApiModelProperty("修改人")
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateUser;
//是否删除
@ApiModelProperty("删除标识")
private Integer isDeleted;
}
使用在请求的方法上
package com.zcl.reggie.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zcl.reggie.common.R;
import com.zcl.reggie.dto.SetmealDto;
import com.zcl.reggie.entity.Category;
import com.zcl.reggie.entity.Setmeal;
import com.zcl.reggie.service.CategoryService;
import com.zcl.reggie.service.SetmealDishService;
import com.zcl.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.CacheManager;
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;
/**
* 项目名称:reggie_take_out
* 描述:套餐管理控制器
*
* @author zhong
* @date 2022-08-04 11:14
*/
@Slf4j
@RestController
@RequestMapping("/setmeal")
@Api(tags = "套餐相关接口")
public class SetmealController {
@Autowired
private SetmealService setmealService;
@Autowired
private SetmealDishService setmealDishService;
/**
* 注入分类对象
*/
@Autowired
private CategoryService categoryService;
/**
* 前端页面根据条件查询套餐信息
* @param setmeal
* @return
*/
@Cacheable(value = "setmealCache",key = "#setmeal.categoryId + '_' + #setmeal.status")
@GetMapping("/list")
@ApiOperation("前端页面根据条件查询套餐信息接口")
public R<List<Setmeal>> list(Setmeal setmeal){
// 1、封装查询条件
LambdaQueryWrapper<Setmeal> lambda = new LambdaQueryWrapper<>();
lambda.eq(setmeal.getCategoryId() != null,Setmeal::getCategoryId,setmeal.getCategoryId()).eq(setmeal.getStatus() != null,Setmeal::getStatus,setmeal.getStatus());
lambda.orderByDesc(Setmeal::getCreateTime);
// 查询出套餐列表数据
List<Setmeal> setmealList = setmealService.list(lambda);
return R.success(setmealList);
}
/**
* 根据传递的is进行删除套餐
* @param ids
* @return
*/
@CacheEvict(value = "setmealCache",allEntries = true)
@DeleteMapping
@ApiOperation(value = "根据传递的is进行删除套餐接口")
public R<String> delete(@RequestParam List<Long> ids){
log.info("接收到需要删除套餐的id:{}",ids);
setmealService.removeWithDish(ids);
return R.success("删除套餐数据成功");
}
/**
* 套餐的分页查询
* @param page
* @param pageSize
* @param name
* @return
*/
@GetMapping("/page")
@ApiOperation(value = "套餐的分页查询接口")
@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){
// 1、f分页构造器
Page<Setmeal> pageInfo = new Page<>(page, pageSize);
// 真正返回的封装数据】
Page<SetmealDto> dtoPage = new Page<>();
// 2、创建分页查询对象【模糊查询】
LambdaQueryWrapper<Setmeal> lambdaQuery = new LambdaQueryWrapper<>();
lambdaQuery.eq(name != null,Setmeal::getName,name);
// 根据创建时间倒排序
lambdaQuery.orderByDesc(Setmeal::getCreateTime);
setmealService.page(pageInfo,lambdaQuery);
// 对象拷贝
BeanUtils.copyProperties(pageInfo,dtoPage,"records");
List<Setmeal> records = pageInfo.getRecords();
// 遍历获取我们需要的对象
List<SetmealDto> list = records.stream().map((item) -> {
SetmealDto setmealDto = new SetmealDto();
// 拷贝对象
BeanUtils.copyProperties(item,setmealDto);
// 获取分类的id
Long categoryId = item.getCategoryId();
// 根据分类的id查询分类对象
Category category = categoryService.getById(categoryId);
// 判断不为空的收获
if(category != null){
// 赋值分了名称
String categoryName = category.getName();
setmealDto.setCategoryName(categoryName);
}
return setmealDto;
}).collect(Collectors.toList());
// 给返回对象重新赋值
dtoPage.setRecords(list);
return R.success(dtoPage);
}
/**
* 新增套餐数据保存
* @param setmealDto
* @return
*/
@CacheEvict(value = "setmealCache",allEntries = true)
@PostMapping
@ApiOperation(value = "新增套餐数据保存接口")
public R<String> save(@RequestBody SetmealDto setmealDto){
log.info("进入套餐新增保存:{}",setmealDto);
setmealService.saveWithDish(setmealDto);
return R.success("新增套餐数据成功");
}
}
再次启动项目访问生成的文档信息
7、项目拆分部署
7.1、部署架构
1、nginx前端部署服务器
2、tomcat后端代码部署服务器
3、MySQL主从复制,两台服务器
4、redis缓存服务器
也可以使用docker进行开发,减少服务器的使用
7.2、部署环境说明
服务器1:
Nginx:部署前端项目、配置反向代理
MySQL:主从复制结果中的主库
服务器2:
jdk:运行java项目
git:版本控制工具
maven:项目构建工具
jar:SpringBoot项目打包成jar包基于内置Tomcat运行
MySQL:主从复制结构中的从库
服务器三:
Redis:缓存中间件
7.3、前端项目部署
将前端项目进行打包然后上传到Linux虚拟机的nginx的html文件中
前端有前端的打包方式,可以自己去了解一下
修改配置文件
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html/dist;
# root html;
index index.html;
# proxy_pass http://targetserver;
}
# 反向代理配置,将请求转发到指定的服务
location ^~ /api/ {
# api重写,正则表达式删除api
# /api/employee/login -----> /employee/login
rewrite ^/api/(.*)$ /$1 break;
proxy_pass http://targetserver;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
刷新配置文件和启动nginx访问宿主机ip80端口,即可成功
7.4、后端项目部署
上传jar包后台运行,或者使用自动部署脚本来完成拉取和启动