SpringBoot前后端分离开发拆分和部署

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包后台运行,或者使用自动部署脚本来完成拉取和启动

  • 17
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论
我的网站结构为前后端分离,所以会产生前后端两个子项目: | 后端 | 前端 | |--------------------------------------------------------------------------------|------------------------------------------------------------------------------------------| | 所在目录:[renfeid-service](./renfeid-service) | 所在目录:[renfeid-ui](./renfeid-ui) | | 技术栈:Java17、SpringBoot3.x、MyBatis、MariaDB、Druid、RestfulAPI、Maven | 技术栈: Node.js、 React、 Next.js、AntD | | 简要说明:尝试过SpringCloud微服务架构,但对于个人网站来说过于沉重和耗费资源,所以我用maven多模块的方式进行了拆分,将来可以改造为微服务架构。 | 简要说明:之前一直没有使用Vue/React是因为SPA对SEO不友好,但随着前端技术的完善,Next的SSR服务器端渲染已经很成熟了,本次是我第一次尝试前台也使用React。 | | 自我点评:采用前后端分离架构,是目前行业比较流行的结构,SpringBoot也是最新版本的依赖。 | 自我点评:本人非前端专业,只是业余学习,很可能使用姿势不符合行业规范,前端项目仅供参考。 | ## 注意事项 后端的主键ID一般为 Long 类型,在 Java 中是 64位整数型,而在 JavaScript 中数字是 64位浮点数,这就造成了精度不一致! 所以,在 JavaScript 中要使用字符串来处理后端的 Long 类型数字,否则会出现意想不到的情况。 ## 目录结构 ### 后端项目目录结构 为啥不选微服务?我尝试过微服务架构,并不适合我,微服务过于沉重,并且在低并发的场景中性能并不如单体应用好,并且造成运维代价大幅提升。 那为啥还参考微服务划分多个模块?虽然我的场景不适合微服务,但我的程序保留微服务化的能力,如果需要改造可以快速的微服务化、分布式部署。 <details> <summary>点此展开查看详情</summary> | 工程名 | 描述 | |--------------------------------------|-------------------------| | + renfeid-bpm | 流程引擎服务(待开发) | | + renfeid-cms | 内容管理服务(CMS) | |    - renfeid-cms-api | 内容管理服务接口 | |    - re ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。
这是一个关于weixin092二手闲置交易市场项目的资源介绍。该项目使用了SSM(SpringSpringMVC、MyBatis)框架进行开发,实现了一个基于微信公众号的二手闲置交易平台。以下是该项目的一些主要功能和技术特点: 用户管理:用户可以注册、登录、修改个人信息等。同时,平台还提供了用户之间的私信功能,方便买卖双方进行沟通。 商品管理:用户可以发布自己的闲置物品,包括上传图片、设置价格、填写商品描述等。同时,还可以浏览其他用户发布的商品,并进行购买。 订单管理:平台提供了订单管理功能,用户可以查看自己的购买记录和销售记录。同时,平台还会对订单进行状态跟踪,如待付款、待发货、已完成等。 搜索功能:用户可以通过关键词搜索感兴趣的商品,提高购物效率。 评论功能:购买商品后,用户可以对商品进行评价,为其他用户提供参考。 微信支付:平台支持微信支付,为用户提供便捷的支付方式。 数据安全:使用MyBatis作为数据访问层框架,保证了数据的安全性和稳定性。 模块化开发项目采用了模块化开发方式,将不同功能模块进行拆分,便于后期维护和升级。 响应式设计:项目采用了响应式设计,可以适应不同设备的屏幕尺寸,为用户提供良好的用户体验。 总之,weixin092二手闲置交易市场项目是一个功能完善、技术先进的交易平台,为用户提供了便捷的二手物品交易服务。
Weixin128在线视频教育系统是一个基于SSM(SpringSpringMVC、MyBatis)框架的在线教育平台。该系统采用了当下流行的微服务架构,将各个功能模块进行拆分,实现了高内聚、低耦合的设计。以下是关于该系统的一些技术特点和功能介绍: 技术特点: 使用Spring框架作为整个项目的核心,实现了依赖注入、AOP等功能,提高了代码的可维护性和可扩展性。 采用SpringMVC作为Web层框架,简化了Controller的编写,提高了开发效率。 使用MyBatis作为持久层框架,实现了与数据库的高效交互,同时支持多种数据库类型。 引入了Redis作为缓存数据库,提高了系统的性能和响应速度。 使用RabbitMQ作为消息队列,实现了异步通信,降低了系统的耦合度。 采用Docker容器化部署,方便快速地搭建和部署环境。 功能介绍: 用户管理:包括用户注册、登录、修改个人信息、密码找回等功能。 课程管理:包括课程分类、课程搜索、课程详情、课程收藏等功能。 学习管理:包括选课、学习进度、学习笔记、课程讨论等功能。 考试管理:包括在线考试、成绩查询、错题回顾等功能。 订单管理:包括购买课程、支付方式、订单查询等功能。 教师管理:包括教师入驻、课程发布、学生管理、课程收益等功能。 系统管理:包括权限管理、日志管理、数据统计等功能。 通过以上介绍,可以看出Weixin128在线视频教育系统是一个功能完善、技术先进的在线教育平台。无论是对于学生还是教师,都能提供良好的使用体验。
### 回答1: 前后端分离项目需要把web项目中的后端代码与前端代码拆分开,其中前端代码使用JavaScript语言,包括HTML、CSS等,后端代码使用Java语言,用于处理数据库的请求等。可以使用RESTful API框架,如SpringMVC来实现前后端的解耦。 ### 回答2: 将Java Web项目拆分前后端分离项目需要执行以下步骤: 1. 分离后端代码:在原有的Java Web项目中,将后端的业务逻辑和数据访问层单独抽离出来,形成独立的后端项目。这个后端项目可以是一个独立的Java项目,使用Spring BootSpring MVC等框架开发后端项目负责处理请求、实现业务逻辑和与数据库交互等操作。 2. 创建前端项目:使用前端开发技术,如HTML、CSS和JavaScript,创建一个新的前端项目。可以使用流行的前端框架,如React、Angular或Vue.js等,在前端项目中负责用户界面的展示和用户交互。 3. 前后端交互:定义前后端数据交互的接口,可以使用RESTful API进行前后端通信。前端发送请求至后端后端接收并处理请求,并将处理结果返回给前端,前端根据返回结果进行逻辑处理和界面展示。 4. 部署和联调:前端项目可以独立部署在一个Web服务器或者静态文件服务器上,后端项目则可以部署在另一个服务器上。前后端项目需要在联调过程中确保能够正常通信,前端能够访问后端接口,并正确显示数据和处理业务逻辑。 5. 优化和维护:在前后端分离项目中,前端和后端可以并行开发和优化,提高开发效率。同时,前端人员可以专注于用户界面的设计和优化,后端人员可以专注于业务逻辑和数据处理的优化。对于不同的模块和功能,可以分配不同的团队进行开发和维护。 通过以上步骤,将原有的Java Web项目拆分前后端分离项目,可以更好地分配资源和团队,提高开发效率和项目维护性,并且使得前端和后端能够独立升级和部署。 ### 回答3: 将一个JavaWeb项目拆分前后端分离项目,主要需要进行以下步骤: 1. 前端与后端项目分离:将原来的JavaWeb项目中的前端页面与后端代码进行拆分,分别放在两个独立的项目中。 2. 前端项目搭建:在前端项目中使用前端框架,如Vue.js、React等,搭建前端页面,并实现与后端的数据交互。 3. 后端项目搭建:使用Spring BootSpring MVC等框架搭建后端项目,提供接口供前端调用,并处理业务逻辑。 4. 接口定义与规范:前后端分离项目中,前端通过接口与后端进行数据交互。因此,需要定义接口的请求方式、参数、返回格式等规范,确保前后端的数据交互正常。 5. 接口调用:前端通过异步请求方式(如Ajax、Fetch等)调用后端接口,并获取返回的数据进行展示或处理。 6. 前后端联调:在开发过程中,前端需要根据定义的接口规范进行接口调用,与后端进行联调,确保前后端的数据交互正常。 7. 部署与测试:将前后端分离项目分别进行部署,并进行功能与性能测试,确保项目的稳定运行。 总结起来,将JavaWeb项目拆分前后端分离项目,需要进行前端项目搭建、后端项目搭建、接口定义与规范、接口调用、前后端联调、部署与测试等步骤。通过这些步骤,可以将原有的JavaWeb项目改造为前后端分离项目,实现前后端的解耦与独立开发

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小钟要学习!!!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值