欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Springboot 前后端分离项目中遇见的一个小问题

程序员文章站 2022-05-03 20:50:06
...

在springboot前后端分离项目中遇见前端代码不能刷新的问题

在学习springboot前后端分离项目时,参照教程1简单实现了一个前后端登录页面。

教程1其实是一个伪前后端分离项目,但是对我们理解前后端分离项目的开发有一定的意义。

虽然具体的开发过程和项目结果和教程中的类似,但是作为项目记录,我还是想将我所有的开发流程在这里重写一遍,并将遇见的问题一一列举出来,作为一个记录。

1. 创建数据库表结构

Springboot 前后端分离项目中遇见的一个小问题

表结构如图,基本就是一个登陆/注册的基本信息收集

2. 创建Springboot项目

​ 本项目的构建过程如下:本项目使用IDEA作为主要开发工具,构建过程如下

​ 2.1 创建新项目

Springboot 前后端分离项目中遇见的一个小问题

2.2 使用Spring Initializr 构建springboot项目
Springboot 前后端分离项目中遇见的一个小问题

​ 如果选择的是Spring构建的就是spring项目而非springboot项目

​ 这里,SDK选择本地的java版本,Initializr Server URL默认就像,确保网速稳定即可。【也可以选择Custom但是我没有使用过,具体可以查查网上的使用教程】

2.3 选择版本
Springboot 前后端分离项目中遇见的一个小问题

这里Group和Artifact就是项目名称了,后期创建项目文件夹会以Artifact的名字创建,里面不要包含特殊字符就行

Type类型默认就行,

Language默认,

Packaging是选择打包模式,一般有jar和war。默认就行

JavaVersion,选择本地java版本就行,我选择8,这里默认是IDEA自带的版本

其他的内容默认就行,因为这是一个测试项目而非正式项目,所以大部分内容都是默认即可

2.4 选择依赖内容

Springboot 前后端分离项目中遇见的一个小问题

因为这是一个前后端分离项目,所以我没有选择模板引擎的内容,如果选择了模板引擎一般使用的是Thymeleaf

这里 数据库选择的是Mysql,数据库操作框架选择是Mybatis.

因为是一个web应用,因此选择的了Springweb。

一般我们可能还需要选择Spring Devtools以用来进行热启动选项。

选择了上述依赖后,点击下一步

2.5 选择项目位置,创建项目

Springboot 前后端分离项目中遇见的一个小问题

3 项目结构

项目结构如图所示

Springboot 前后端分离项目中遇见的一个小问题

因为我是用的是Mybatis框架操作的数据库内容,因此这里需要一个针对Mybatis的xml配置文件用来操作数据。

特别说明:】不同于eclipse,在IDEA中,我们需要将xml文件存放在resources文件夹中,而不能存在java文件中,不然无论你在application.properties如何配置mybatis的xml文件搜索路径都无法将xml文件编译到target目录内。这是因为IDEA默认希望所有的资源文件都在Resources目录下,java文件内只用写代码即可。也算是一个小坑吧。

4 具体代码

4.1 POJO包

4.1.1 User类

​ 构建User类,完成对数据库的映射,以及完成对前端数据的接受工作

​ User类的内容如下:

package com.example.springbootdemoforweb2.Pojo;

/**
 * 一个pojo类
 */
public class User {

    private int id;
    private String name;
    private String passwd;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPasswd() {
        return passwd;
    }

    public void setPasswd(String passwd) {
        this.passwd = passwd;
    }
}

4.1.2 构建后端返回给前端的数据结构类Result

​ Result类用于构建一个数据结构,这个结构包含了后端返回给前端的数据,将其组织起来,然后依据Controller层的@ResponseBody注解,将处理结果包装成json对象,返回给前端页面.完成对前后端数据的交互.

package com.example.springbootdemoforweb2.Pojo;

/**
 * 定义后端向前端发送的数据结构
 */
public class Result<T> { // 这里T代表可以返回任意数据类型

    private String msg;
    private boolean success;
    private T detail;

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public T getDetail() {
        return detail;
    }

    public void setDetail(T detail) {
        this.detail = detail;
    }
}

4.2 Mapper层

4.2.1 构建UserMapper 接口,定义数据库操作方法

package com.example.springbootdemoforweb2.Mapper;

import com.example.springbootdemoforweb2.Pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * 构造UserMapper 操控数据库
 */
@Mapper
@Repository
public interface UserMapper {

    public List<User> findAll();
    public User findByName(User user);
    public int delete(User user);
    public void put(User user);
    public Integer login(User user); // 这里使用Interger而不是Int是因为返回结果可能为空
    public void regis(User user);
}

​ 这里是一个接口 不需要实例化,Springboot会自动实例化这个接口并完成相关功能。因此我们只需要定义一个接口即可。

​ 这里需要注意的是,我们不仅在UserMapper上使用了@Mapper注解,也使用了@Repository注解,之所以使用@Repository注解是因为在service层自动注入UserMapper对象时可能会报错,具体原因目前还没有弄清楚,有待观察。

4.3 Service层

4.3.1 在构建好Mapper层后,构建Service层完成相关具体的服务工作

package com.example.springbootdemoforweb2.Service;

import com.example.springbootdemoforweb2.Mapper.UserMapper;
import com.example.springbootdemoforweb2.Pojo.Result;
import com.example.springbootdemoforweb2.Pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 服务提供方
 */
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    public Result login(User user){

        Result result = new Result();
        result.setSuccess(false);
        result.setDetail(null);

        try {

            Integer id = userMapper.login(user);

            if (id!=null){
                // 代表查找成功
                result.setSuccess(true);
                result.setMsg("登录成功");
                result.setDetail(userMapper.findByName(user));
            }else{
                result.setMsg("登录失败,该用户或者密码错误");
            }


        }catch (Exception e){
            result.setMsg(e.getMessage());
            e.printStackTrace();
        }

        return result;

    }

    public Result registry(User user){

        Result result = new Result();
        result.setSuccess(false);
        result.setDetail(null);

        try {

            User exituser = userMapper.findByName(user);
            if (exituser!=null){
                //当该用户名已经存在,返回注册失败
                result.setMsg("该用户名已经存在");
            }else {
                userMapper.regis(user);
                result.setSuccess(true);
                result.setMsg("注册成功");
                result.setDetail(userMapper.findByName(user));
            }

        }catch (Exception e){
            result.setMsg(e.getMessage());
            e.printStackTrace();
        }

        return result;
    }

}

4.4 Mybatis配置文件

4.4.1 构建UserMapper.xml配置文件

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.example.springbootdemoforweb2.Mapper.UserMapper">

    <!--        查找所有内容-->
    <select id="findAll" resultType="User">
            select * from user_info
        </select>

    <select id="findByName" parameterType="User" resultType="User">
            select * from user_info where name = #{name}
        </select>

    <select id="login" parameterType="User" resultType="int">
            select id from user_info where name=#{name} and passwd=#{passwd}
        </select>

    <insert id="regis" parameterType="User" >
            insert into user_info (name,passwd) values (#{name},#{passwd})
        </insert>

    <delete id="delete" parameterType="User">
            delete from user_info where name = #{name} and passwd = #{passwd}
        </delete>

    <update id="update" parameterType="User">
            update user_info set passwd = #{passwd} where name=#{name}
        </update>


</mapper>

​ 这里,我们构建一个UserMapper.xml的配置文件,该文件是Mybatis的核心配置文件。其中namespace需要只想UserMapper这个借口,需要使用全局限定名称。然后再标签内写下所有操作数据库的方法。其中id就是UserMapper的方法名

4.5 Controller层

4.5.1 构建UserController完成功能映射

package com.example.springbootdemoforweb2.Controller;

import com.example.springbootdemoforweb2.Pojo.Result;
import com.example.springbootdemoforweb2.Pojo.User;
import com.example.springbootdemoforweb2.Service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping("/login")
    @ResponseBody
    public Result login(User user){
        System.out.println("用户姓名:"+user.getName()+"\t用户密码:"+user.getPasswd());
        return userService.login(user);
    }

    @RequestMapping("/registry")
    @ResponseBody
    public Result regis(User user){
        return userService.registry(user);
    }


}

​ 功能映射配置很简单,这里使用@ResponseBody将函数的方法转换为json格式

​ 如果使用了Thymeleaf,并将@ResponseBody删除,在propertis配置文件中指定了默认网页地址,就可以通过返回的名称直接定位到指定的网页中。不过这个是前后端不分离的做法。

​ 在前后端分离的开发过程中,前后端一般通过json传递数据内容。因此这里可以通过配置@ResponseBody将返回的Result类型构造为Json格式

4.6 application.properties配置文件

# mysql配置
spring.datasource.url=jdbc:mysql://localhost:3306/zyc_web_test?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.max-idle=10
spring.datasource.max-wait=10000
spring.datasource.min-idle=5

# 端口配置
server.port=9090

# 静态资源配置
spring.resources.static-locations=classpath:/static

# 配置mybatis环境
mybatis.type-aliases-package=com.example.springbootdemoforweb2.Pojo
mybatis.mapper-locations=classpath:MapperXML/*.xml

该配置文件是springboot的核心配置文件,通过该配置文件可以设定springboot项目的各种信息。

这里我们需要注意到的是mybatis环境配置信息,其中mapper-locations配置了xml问津的路径,type-aliases-package配置了xml文件类resultType的简写路径。

其他的像Mysql配置,端口配置,静态资源配置就很容易理解了

4.7 静态文件

4.7.1 登录页面 login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
    <form action="/login" method="post">
        <p>用户名:<input name="name" type="text"></p>
        <p>密码:<input name="passwd" type="password"></p>
        <input type="submit" value="登录">
    </form>
</body>
</html>

这里是登录页面,因为这是一个简单地前后端分离登录页面,因此前端项目没有单独使用一个服务器部署

4.7.2 注册页面registry.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册页面</title>
</head>
<body>

    <form action="/registry" method="post">
        <p>用户名:<input name="name" type="text"></p>
        <p>密码:<input name="passwd" type="password"></p>
        <input type="submit" value="注册">
    </form>

</body>
</html>

这里是注册页面

5 遇见的问题

其实,一开始登录页面以及注册页面中表格代码不是如上所示的内容,其初始内容如下:

注册页面
<form action="/registry" method="post">
    <p>用户名:<input name="username" type="text"></p>
    <p>密码:<input name="passwd" type="password"></p>
    <input type="submit" value="注册">
</form>

这里用户名的name属性是username,而不是完成版中的name。需要注意的是前端通过POST方法向后端提交数据时,后端可以通过getparameter方法获取指定name属性的值。而一开始,name属性的值是username而非User对象中的name,导致后端无法正确接收用户姓名。

​ 当将前端的name属性修改为name后发现还是无法正确接收用户姓名属性。在target编译文件中可以发现login.html和registry.html中相关属性已经修改成功。最后在chrome中发现,原来是chrome的缓存机制导致修改后的网页并没有立刻呈现出修改后的结果,其name属性仍然是username而非name

​ 其解决方法如下:

  1. 点击F12打开开发者模式

  2. 点击右上角的三个点

  3. Springboot 前后端分离项目中遇见的一个小问题

  4. 点击设置

  5. Springboot 前后端分离项目中遇见的一个小问题

  6. 点击

  7. Springboot 前后端分离项目中遇见的一个小问题
    点击如上两个选项,让chrome缓存失效,保证每一次的页面都是最新的页面即可。