SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

时间:2021-06-24 13:24:06

相关

(1) 相关博文地址:

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 前端篇(一):搭建基本环境:https://www.cnblogs.com/l-y-h/p/12930895.html
SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 前端篇(二):引入 element-ui 定义基本页面显示:https://www.cnblogs.com/l-y-h/p/12935300.html
SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 前端篇(三):引入 js-cookie、axios、mock 封装请求处理以及返回结果:https://www.cnblogs.com/l-y-h/p/12955001.html
SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 前端篇(四):引入 vuex 进行状态管理、引入 vue-i18n 进行国际化管理:https://www.cnblogs.com/l-y-h/p/12963576.html
SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 前端篇(五):引入 vue-router 进行路由管理、模块化封装 axios 请求、使用 iframe 标签嵌套页面:https://www.cnblogs.com/l-y-h/p/12973364.html
SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 前端篇(六):使用 vue-router 进行动态加载菜单:https://www.cnblogs.com/l-y-h/p/13052196.html

(2)代码地址:

https://github.com/lyh-man/admin-vue-template.git

一、简介

1、项目介绍

(1)基本介绍
  使用 SpringBoot + MyBatisPlus + MySQL 搭建一个后台管理系统的模板(后台代码)。
  当然这类模板在网上有很多,可以直接下载、修改使用。
  写这个项目目的,纯属练手(有不对的地方,还望不吝指教)。

【layuiAdmin 后台管理模板:(付费)】
https://www.layui.com/admin/ 【renren-fast:(开源)】
https://gitee.com/renrenio/renren-fast

(2)软件使用
开发工具(随意选择,能提高开发效率即可):
  IntelliJ IDEA + Navicat

【Win10 下载、安装 Navicat 15 并激活 参考:】
https://www.cnblogs.com/l-y-h/p/12746596.html

环境、依赖:
  JDK 1.8                        开发环境
  SpringBoot 2.3             核心框架
  SpringMVC                  视图框架
  MyBatisPlus                持久层框架
  swagger2                     生成在线的接口文档
  JSR303                        数据校验
  SLF4J、Logback         日志管理
  MySQL 8.0.18              数据库

【mysql安装、使用 -- windows 参考:】
https://www.cnblogs.com/l-y-h/p/11700113.html 【mysql安装、使用 -- Linux 参考:】
https://www.cnblogs.com/l-y-h/p/12576633.html 【docker 安装 mysql 8.0.18 -- Linux 参考:】
https://www.cnblogs.com/l-y-h/p/12622730.html#_label5

(3)基本目录介绍

back
|--- src
| |--- main 保存源代码
| | |--- java 代码目录
| | | |--- common 保存公共操作
| | | | |--- config 保存配置类
| | | | |--- exception 保存异常处理操作
| | | | |--- utils 保存工具类
| | | | |--- validator 保存 JSR303 校验相关操作
| | | |--- controller 保存控制层代码
| | | |--- entity 保存实体类代码
| | | |--- handler 保存数据处理相关操作
| | | |--- mapper 保存 sql 相关映射操作
| | | |--- service 保存业务层代码
|
| | |--- resources 资源目录
| | | |--- static 用于保存项目静态文件
| | | |--- application.yml 用于保存项目的配置信息
| | | |--- logback-spring.xml 用于保存日志的配置信息
|
| |--- test 保存测试代码
| | |--- java
|
|--- pom.xml 用于保存项目依赖信息

二、基本环境搭建

1、创建一个 SpringBoot 项目

Step1:
  File -》 New -》Project

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step2:
  Spring Initializr -》 选择合适的 JDK -》next

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step3:
  填写相关信息。

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step4:
  选择相应的 SpringBoot 版本,以及相关依赖(可以在项目中手动添加)。
如下,选择一个 web 依赖(自动集成SpringMVC 相关依赖)。

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step5:
  选择项目保存的路径。点击 next 后等待软件下载项目相关依赖即可。

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

2、项目构建 -- 统一结果处理

(1)简介
  定义统一数据规范,返回一个固定格式的 JSON 结果。

【格式:】
是否响应成功(success: true / false)
  响应状态码(code:200 / 400 / 500 等)
  状态码描述(message:访问成功 / 系统异常等)
  响应数据(data:处理的数据) 【类似如下:】
{
"success": true,
"code": 200,
"message": "success",
"data": {}
}

可以参考:
  https://www.cnblogs.com/l-y-h/p/12781586.html#_label1

(2)代码实现
Step1:
  添加依赖信息。
  httpcore 用于状态码的获取。
  lombok 用于简化代码的编写。

<!-- 状态码参考地址:http://hc.apache.org/httpcomponents-core-ga/httpcore/apidocs/org/apache/http/HttpStatus.html -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.13</version>
</dependency> <dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step2:
  在 common.utils 目录下新建一个 Result.java 工具类,用于封装返回结果。

package com.lyh.admin_template.back.common.utils;

import lombok.Data;
import org.apache.http.HttpStatus; import java.util.HashMap;
import java.util.Map; /**
* 统一结果返回类。方法采用链式调用的写法(即返回类本身 return this)。
* 构造器私有,不允许进行实例化,但提供静态方法 ok、error 返回一个实例。
* 静态方法说明:
* ok 返回一个 成功操作 的结果(实例对象)。
* error 返回一个 失败操作 的结果(实例对象)。
*
* 普通方法说明:
* success 用于自定义响应是否成功
* code 用于自定义响应状态码
* message 用于自定义响应消息
* data 用于自定义响应数据
*
* 依赖信息说明:
* 此处使用 @Data 注解,需导入 lombok 相关依赖文件。
* 使用 HttpStatus 的常量表示 响应状态码,需导入 httpcore 相关依赖文件。
*/
@Data
public class Result {
/**
* 响应是否成功,true 为成功,false 为失败
*/
private Boolean success; /**
* 响应状态码, 200 成功,500 系统异常
*/
private Integer code; /**
* 响应消息
*/
private String message; /**
* 响应数据
*/
private Map<String, Object> data = new HashMap<>(); /**
* 默认私有构造器
*/
private Result(){} /**
* 私有自定义构造器
* @param success 响应是否成功
* @param code 响应状态码
* @param message 响应消息
*/
private Result(Boolean success, Integer code, String message){
this.success = success;
this.code = code;
this.message = message;
} /**
* 返回一个默认的 成功操作 的结果,默认响应状态码 200
* @return 成功操作的实例对象
*/
public static Result ok() {
return new Result(true, HttpStatus.SC_OK, "success");
} /**
* 返回一个自定义 成功操作 的结果
* @param success 响应是否成功
* @param code 响应状态码
* @param message 响应消息
* @return 成功操作的实例对象
*/
public static Result ok(Boolean success, Integer code, String message) {
return new Result(success, code, message);
} /**
* 返回一个默认的 失败操作 的结果,默认响应状态码为 500
* @return 失败操作的实例对象
*/
public static Result error() {
return new Result(false, HttpStatus.SC_INTERNAL_SERVER_ERROR, "error");
} /**
* 返回一个自定义 失败操作 的结果
* @param success 响应是否成功
* @param code 响应状态码
* @param message 相应消息
* @return 失败操作的实例对象
*/
public static Result error(Boolean success, Integer code, String message) {
return new Result(success, code, message);
} /**
* 自定义响应是否成功
* @param success 响应是否成功
* @return 当前实例对象
*/
public Result success(Boolean success) {
this.setSuccess(success);
return this;
} /**
* 自定义响应状态码
* @param code 响应状态码
* @return 当前实例对象
*/
public Result code(Integer code) {
this.setCode(code);
return this;
} /**
* 自定义响应消息
* @param message 响应消息
* @return 当前实例对象
*/
public Result message(String message) {
this.setMessage(message);
return this;
} /**
* 自定义响应数据,一次设置一个 map 集合
* @param map 响应数据
* @return 当前实例对象
*/
public Result data(Map<String, Object> map) {
this.data.putAll(map);
return this;
} /**
* 通用设置响应数据,一次设置一个 key - value 键值对
* @param key 键
* @param value 数据
* @return 当前实例对象
*/
public Result data(String key, Object value) {
this.data.put(key, value);
return this;
}
}

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

(3)编写一个 TestController.java 用于测试一下。
代码:

package com.lyh.admin_template.back.controller;

import com.lyh.admin_template.back.common.utils.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; /**
* 用于测试环境搭建各个功能是否成功
*/
@RestController
@RequestMapping("/test")
public class TestController {
/**
* 用于测试 Result 是否能够正常返回指定格式的数据
* @return
*/
@GetMapping("/testResult")
public Result testResult() {
return Result.ok();
}
}

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

启动服务并访问接口,返回结果如下:(返回指定的 JSON 格式)

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

3、项目构建 -- 统一异常处理

(1)简介
  程序运行时可能会遇到各种错误,使用统一的异常处理规范,可以方便的排查错误。
可以参考:
  https://www.cnblogs.com/l-y-h/p/12781586.html#_label2

(2)代码实现
Step1:
  添加依赖信息。
  此处需要使用 @RestControllerAdvice、@ExceptionHandler 两个注解,需要 Spring MVC 相关依赖,若之前创建项目时,未选择 Spring Web,此处需要手动添加。

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

Step2:
  在 common.exception 目录下自定义一个异常类 GlobalException,用于自定义异常信息。

package com.lyh.admin_template.back.common.exception;

import lombok.Data;
import org.apache.http.HttpStatus; /**
* 自定义异常,
* 可以自定义 异常信息 message 以及 响应状态码 code(默认为 500)。
*
* 依赖信息说明:
* 此处使用 @Data 注解,需导入 lombok 相关依赖文件。
* 使用 HttpStatus 的常量表示 响应状态码,需导入 httpcore 相关依赖文件。
*/
@Data
public class GlobalException extends RuntimeException {
/**
* 保存异常信息
*/
private String message; /**
* 保存响应状态码
*/
private Integer code = HttpStatus.SC_INTERNAL_SERVER_ERROR; /**
* 默认构造方法,根据异常信息 构建一个异常实例对象
* @param message 异常信息
*/
public GlobalException(String message) {
super(message);
this.message = message;
} /**
* 根据异常信息、响应状态码构建 一个异常实例对象
* @param message 异常信息
* @param code 响应状态码
*/
public GlobalException(String message, Integer code) {
super(message);
this.message = message;
this.code = code;
} /**
* 根据异常信息,异常对象构建 一个异常实例对象
* @param message 异常信息
* @param e 异常对象
*/
public GlobalException(String message, Throwable e) {
super(message, e);
this.message = message;
} /**
* 根据异常信息,响应状态码,异常对象构建 一个异常实例对象
* @param message 异常信息
* @param code 响应状态码
* @param e 异常对象
*/
public GlobalException(String message, Integer code, Throwable e) {
super(message, e);
this.message = message;
this.code = code;
}
}

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step3:
  在 common.exception 目录下定义一个全局异常处理类 GlobalExceptionHandler,用于处理异常。
  异常一般都与日志一起操作,此处演示不使用注解 @Slf4j 时的日志处理。

package com.lyh.admin_template.back.common.exception;

import com.lyh.admin_template.back.common.utils.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice; /**
* 全局异常处理类。
* 使用 slf4j 保存日志信息。
* 此处使用了 统一结果处理 类 Result 用于包装异常信息。
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
private Logger logger = LoggerFactory.getLogger(getClass()); /**
* 处理 Exception 异常
* @param e 异常
* @return 处理结果
*/
@ExceptionHandler(Exception.class)
public Result handlerException(Exception e) {
logger.error(e.getMessage(), e);
return Result.error().message("系统异常");
} /**
* 处理空指针异常
* @param e 异常
* @return 处理结果
*/
@ExceptionHandler(NullPointerException.class)
public Result handlerNullPointerException(NullPointerException e) {
logger.error(e.getMessage(), e);
return Result.error().message("空指针异常");
} /**
* 处理自定义异常
* @param e 异常
* @return 处理结果
*/
@ExceptionHandler(GlobalException.class)
public Result handlerGlobalException(GlobalException e) {
logger.error(e.getMessage(), e);
return Result.error().message(e.getMessage()).code(e.getCode());
}
}

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step4:
  在 TestController.java 添加一个接口,用于测试。

package com.lyh.admin_template.back.controller;

import com.lyh.admin_template.back.common.exception.GlobalException;
import com.lyh.admin_template.back.common.utils.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; /**
* 用于测试环境搭建各个功能是否成功
*/
@RestController
@RequestMapping("/test")
public class TestController { /**
* 用于测试 异常 是否能被统一处理,并返回指定格式的数据
* @return
*/
@GetMapping("/testGlobalException")
public Result testGlobalException() {
try {
int test = 10 / 0;
} catch (Exception e) {
throw new GlobalException(e.getMessage());
}
return Result.ok();
}
}

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

启动服务并访问接口,返回结果如下:(捕获到异常信息并返回)

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

日志也能正常输出:

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step5:(可选操作)
  若想在返回结果中输出 堆栈 信息,可以定义如下工具类,将堆栈信息转为 String 类型,然后再添加到返回的 message 中即可。
如下,新建一个工具类,用于装换堆栈信息为 String 类型,使用 @Slf4j 注解形式操作日志。

package com.lyh.admin_template.back.common.utils;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter; /**
* 异常处理工具类,将异常堆栈信息转为 String 类型
*/
@Slf4j
public class ExceptionUtil {
public static String getMessage(Exception e) {
String message = null;
try(StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)) {
e.printStackTrace(pw);
pw.flush();
sw.flush();
message = sw.toString();
}catch (IOException io) {
io.printStackTrace();
log.error(io.getMessage());
}
return message;
}
}

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

修改 TestController.java,简单测试一下:

package com.lyh.admin_template.back.controller;

import com.lyh.admin_template.back.common.exception.GlobalException;
import com.lyh.admin_template.back.common.utils.ExceptionUtil;
import com.lyh.admin_template.back.common.utils.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; /**
* 用于测试环境搭建各个功能是否成功
*/
@RestController
@RequestMapping("/test")
public class TestController { /**
* 用于测试 异常 是否能被统一处理,并返回指定格式的数据
* @return
*/
@GetMapping("/testGlobalException")
public Result testGlobalException() {
try {
int test = 10 / 0;
} catch (Exception e) {
throw new GlobalException(ExceptionUtil.getMessage(e));
}
return Result.ok();
}
}

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

4、项目构建 -- 统一日志处理

(1)简介
  使用日志可以方便、快速定位到错误所在处。统一日志格式可以更方便日志的查看、管理。
可以参考:
  https://www.cnblogs.com/l-y-h/p/12781586.html#_label3

(2)代码实现:
  在 resources 目录下新建一个 logback-spring.xml 文件,其会在项目启动时被加载。
Step1:
  基本代码如下:(需要适当修改)

<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时,配置文档如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文档是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。
当scan为true时,此属性生效。默认的时间间隔为1分钟。-->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。-->
<configuration scan="true" scanPeriod="10 seconds">
<!-- Step1(可选):定义 logger 上下文名称 -->
<contextName>logback</contextName> <!-- Step2(可选):定义变量 -->
<!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。
定义变量后,可以使 "${}" 来使用变量。 -->
<!-- Step2.1 定义文件输出路径 -->
<!-- 若项目部署到 linux 服务器上,此处路径必须得改 -->
<!-- <property name="log.path" value="E:/myProject/log"/> -->
<!-- 将日志文件输出到 当前目录下的 log 文件夹 -->
<property name="log.path" value="src/log"/> <!-- Step2.2 定义彩色日志以及日志输出格式 -->
<!-- 配置格式变量:CONSOLE_LOG_PATTERN 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN"
value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/> <!-- Step3:定义日志输出位置 -->
<!-- Step3.1 定义日志输出:输出到控制台 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--此日志 appender 是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
<!-- 例如:如果此处配置了 INFO 级别,则后面其他位置即使配置了 DEBUG 级别的日志,也不会被输出 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>DEBUG</level>
</filter>
<encoder>
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender> <!-- Step3.2 定义日志输出:输出到文件 -->
<!-- Step3.2.1 level为 DEBUG 日志,时间滚动输出 -->
<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${log.path}/debug.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志归档 -->
<fileNamePattern>${log.path}/debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文档保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文档只记录debug级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>debug</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender> <!-- Step3.2.2 level为 INFO 日志,时间滚动输出 -->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${log.path}/info.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天日志归档路径以及格式 -->
<fileNamePattern>${log.path}/info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文档保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文档只记录info级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>info</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender> <!-- Step3.2.3 level为 WARN 日志,时间滚动输出 -->
<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${log.path}/warn.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文档保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文档只记录warn级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>warn</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender> <!-- Step3.2.4 level为 ERROR 日志,时间滚动输出 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${log.path}/error.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文档保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文档只记录ERROR级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender> <!-- Step4:配置不同环境下的日志输出 -->
<!-- Step4.1 开发环境:打印到控制台-->
<springProfile name="dev">
<!-- <logger> 用来设置某一个包或者具体的某一个类的日志打印级别
<logger name="com.lyh" level="INFO"/>
-->
<!-- 若想打印 sql 语句,可以指定包,并指定日志级别为 debug -->
<logger name="com.lyh.admin_template.back" level="DEBUG"/> <!-- root 用来指定最基础的日志输出级别 -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="DEBUG_FILE"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="WARN_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</springProfile> <!-- Step4.2 生产环境:输出到文档-->
<springProfile name="prod">
<logger name="com.lyh" level="WARN"/>
<root level="INFO">
<appender-ref ref="ERROR_FILE"/>
<appender-ref ref="WARN_FILE"/>
</root>
</springProfile>
</configuration>

Step2:
  按照项目需要对该文件进行修改。
一般只需要修改两处地方即可。

【修改一:】
// 指定日志输出路径(log.path 相关的地方都可以视情况修改)
<property name="log.path" value="E:/myProject/log"/> <!-- 输出到指定的文件夹下 -->
<property name="log.path" value="src/log"/> <!-- 输出到当前项目下 --> 【修改二:】
// 使用 logger 指定包日志级别管理,根据项目实际的包名进行修改(如下,用于输出 sql 语句)
<logger name="com.lyh.admin_template.back" level="DEBUG"/>

Step3:
  在 application.yml 或者 application.properties 中指定环境(dev、prod)可以打印不同的日志。

spring:
profiles:
active: prod

三、集成一些常用功能

1、SpringBoot 整合 Swagger

(1)简介
  使用 Swagger 生成一个在线的接口文档,前后端可以根据此接口文档对接口进行测试、调用。
Swagger 使用可以参考:
  https://www.cnblogs.com/l-y-h/p/12872748.html

(2)SpringBoot 整合 Swagger
Step1:
  添加依赖信息。

<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step2:
  在 common.config 目录下编写一个配置类 SwaggerConfig.java,用于配置 Swagger 相关信息。
  @Configuration 注解用于标注一个配置类
  @EnableSwagger2 注解用于开启 Swagger。
  @Profile({"dev","test"}) 注解用于表示 dev、test 环境下可以使用 Swagger,prod 环境下不行。
如下:
  指定了 com.lyh.admin_template.back.controller 包下标注了 @ApiOperation 注解的接口才会生成接口文档。

package com.lyh.admin_template.back.common.config;

import io.swagger.annotations.ApiOperation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2; @Configuration
@EnableSwagger2
@Profile({"dev","test"})
public class SwaggerConfig { @Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
// 加了ApiOperation注解的类,才会生成接口文档
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
// 指定包下的类,才生成接口文档
.apis(RequestHandlerSelectors.basePackage("com.lyh.admin_template.back.controller"))
.paths(PathSelectors.any())
.build();
} private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Swagger 测试")
.description("Swagger 测试文档")
.termsOfServiceUrl("https://www.cnblogs.com/l-y-h/")
.version("1.0.0")
.build();
}
}

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step3:
  编写 TestController.java 用于测试一下 Swagger 是否整合成功。

package com.lyh.admin_template.back.controller;

import com.lyh.admin_template.back.common.utils.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; /**
* 用于测试环境搭建各个功能是否成功
*/
@RestController
@RequestMapping("/test")
@Api(tags = "测试页面")
public class TestController { /**
* 用于测试 Swagger 是否整合成功
* @return
*/
@ApiOperation(value = "测试 Swagger")
@GetMapping("/testSwagger")
public Result testSwagger() {
return Result.ok();
}
}

Step4:
  访问项目的 swagger-ui.html 页面,即可访问到 Swagger 页面。
比如:http://localhost:8080/swagger-ui.html,
如下,三个接口,只有标注了 @ApiOperation 注解的接口才会被显示在 接口文档中。

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

2、SpringBoot 整合 MyBatis-Plus

(1)简介
  MyBatisPlus 是 MyBatis 增强版(持久层框架),其内置了一些 sql 增删改查的操作,可以免去 xml 文件的编写工作(当然复杂的 sql 逻辑还是得用 xml 编写)。
  当然还有其他一些功能,总体使用起来比 MyBatis 更加方便。

使用 MyBatisPlus,可参考:
  https://www.cnblogs.com/l-y-h/p/12859477.html

(2)SpringBoot 整合 MyBatisPlus
Step1:
  添加相关依赖信息。
  涉及数据库操作,此处使用 mysql,所以需要 mysql-connector 相关依赖。
  当然还得有 MyBatisPlus 相关操作的依赖。
    mybatis-plus-boot-starter 是最基本的依赖。
    mybatis-plus-generator、velocity-engine-core 是MyBatisPlus 代码生成器所需依赖。

<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
<!-- mybatis-plus-generator -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.3.1.tmp</version>
</dependency>
<!-- mybatis-plus engine -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.2</version>
</dependency>
<!-- mysql-connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step2:
  在本地数据库新建一个 admin_template 数据库,并建立一个 back_user 表用于测试一下 MyBatisPlus 是否能使用。
back_user 建表 SQL 语句如下:

DROP DATABASE IF EXISTS admin_template;

CREATE DATABASE admin_template;

USE admin_template;

CREATE TABLE back_user (
id bigint NOT NULL COMMENT '用户 ID',
name varchar(50) NOT NULL COMMENT '用户名',
mobile varchar(20) NOT NULL COMMENT '用户手机号',
password varchar(64) NOT NULL COMMENT '用户密码',
create_time datetime COMMENT '创建时间',
update_time datetime COMMENT '修改时间',
delete_flag tinyint COMMENT '逻辑删除标志,0 表示未删除, 1 表示删除',
version tinyint COMMENT '版本号',
PRIMARY KEY(id),
UNIQUE INDEX(name)
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4 COMMENT='用户表';

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step3:
  在配置文件中配置 数据源信息。
  根据项目自行配置 mysql 数据源信息。

spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
url: jdbc:mysql://localhost:3306/admin_template?useUnicode=true&characterEncoding=utf8

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step4:
  在项目启动类上,需要标注 @MapperScan 注解用于指明需要被扫描的 mapper 的路径。

package com.lyh.admin_template.back;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication
@MapperScan("com.lyh.admin_template.back.mapper")
public class BackApplication { public static void main(String[] args) {
SpringApplication.run(BackApplication.class, args);
} }

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

(3)使用代码生成器
  使用 MyBatisPlus 代码生成器 AutoGenerator 根据 back_user 表逆向生成基本代码。
Step1:
  添加依赖。
  mybatis-plus-generator、velocity-engine-core 是MyBatisPlus 代码生成器所需依赖。

<!-- mybatis-plus-generator -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.3.1.tmp</version>
</dependency>
<!-- mybatis-plus engine -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.2</version>
</dependency>

Step2:
  编写一个测试类,用于测试 代码生成器 功能。
  需要修改的地方,下面注释中已标明。

package com.lyh.admin_template.back;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest; @SpringBootTest
public class TestAutoGenerator {
@Test
public void autoGenerate() {
// Step1:代码生成器
AutoGenerator mpg = new AutoGenerator(); // Step2:全局配置
GlobalConfig gc = new GlobalConfig();
// 填写代码生成的目录(需要修改)
String projectPath = "E:\\myProject\\myGit\\admin-vue-template\\back";
// 拼接出代码最终输出的目录
gc.setOutputDir(projectPath + "/src/main/java");
// 配置开发者信息(可选)(需要修改)
gc.setAuthor("lyh");
// 配置是否打开目录,false 为不打开(可选)
gc.setOpen(false);
// 实体属性 Swagger2 注解,添加 Swagger 依赖,开启 Swagger2 模式(可选)
gc.setSwagger2(true);
// 重新生成文件时是否覆盖,false 表示不覆盖(可选)
gc.setFileOverride(false);
// 配置主键生成策略,此处为 ASSIGN_ID(可选)
gc.setIdType(IdType.ASSIGN_ID);
// 配置日期类型,此处为 ONLY_DATE(可选)
gc.setDateType(DateType.ONLY_DATE);
// 默认生成的 service 会有 I 前缀
gc.setServiceName("%sService");
mpg.setGlobalConfig(gc); // Step3:数据源配置(需要修改)
DataSourceConfig dsc = new DataSourceConfig();
// 配置数据库 url 地址
dsc.setUrl("jdbc:mysql://localhost:3306/admin_template?useUnicode=true&characterEncoding=utf8");
// dsc.setSchemaName("testMyBatisPlus"); // 可以直接在 url 中指定数据库名
// 配置数据库驱动
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
// 配置数据库连接用户名
dsc.setUsername("root");
// 配置数据库连接密码
dsc.setPassword("123456");
mpg.setDataSource(dsc); // Step:4:包配置
PackageConfig pc = new PackageConfig();
// 配置父包名(需要修改)
pc.setParent("com.lyh.admin_template");
// 配置模块名(需要修改)
pc.setModuleName("back");
// 配置 entity 包名
pc.setEntity("entity");
// 配置 mapper 包名
pc.setMapper("mapper");
// 配置 service 包名
pc.setService("service");
// 配置 controller 包名
pc.setController("controller");
mpg.setPackageInfo(pc); // Step5:策略配置(数据库表配置)
StrategyConfig strategy = new StrategyConfig();
// 指定表名(可以同时操作多个表,使用 , 隔开)(需要修改)
strategy.setInclude("back_user");
// 配置数据表与实体类名之间映射的策略
strategy.setNaming(NamingStrategy.underline_to_camel);
// 配置数据表的字段与实体类的属性名之间映射的策略
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
// 配置 lombok 模式
strategy.setEntityLombokModel(true);
// 配置 rest 风格的控制器(@RestController)
strategy.setRestControllerStyle(true);
// 配置驼峰转连字符
strategy.setControllerMappingHyphenStyle(true);
// 配置表前缀,生成实体时去除表前缀
// 此处的表名为 test_mybatis_plus_user,模块名为 test_mybatis_plus,去除前缀后剩下为 user。
strategy.setTablePrefix(pc.getModuleName() + "_");
mpg.setStrategy(strategy); // Step6:执行代码生成操作
mpg.execute();
}
}

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

执行后,根据 back_user 表结构生成 controller、entity、mapper、service 等相关代码。

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

(4)使用自动填充数据
  添加数据时,按照某种规则自动给字段添加相关数据。
比如:
  back_user 中 create_time、update_time、delete_flag、version 等字段,在插入、修改时都是按照某种数据填充规则进行数据填充,这些数据就可以使用 MyBatisPlus 的自动填充数据功能来完成。
可以参考:
 https://www.cnblogs.com/l-y-h/p/12859477.html#_label1_3

Step1:
  在 handler 目录下新建一个自动填充处理类 MyMetaObjectHandler.java,实现 MetaObjectHandler 接口,并重写方法以实现自动填充功能。
  @Component 表示将此类交给 Spring 去管理。

package com.lyh.admin_template.back.handler;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component; import java.util.Date; /**
* 自动填充处理类,
* insertFill() 表示插入时的填充规则,
* updateFill() 表示更新时的填充规则。
*/
@Component
public class MyMetaObjectHandler implements MetaObjectHandler { /**
* 插入时的填充规则
* @param metaObject
*/
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
this.strictInsertFill(metaObject, "updateTime", Date.class, new Date());
this.strictInsertFill(metaObject, "deleteFlag", Integer.class, 0);
this.strictInsertFill(metaObject, "version", Integer.class, 1);
} /**
* 更新时的填充规则
* @param metaObject
*/
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
}
}

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step2:
  给实体类需要填充的数据上添加 @TableField 注解,并指明填充规则。

package com.lyh.admin_template.back.entity;

import com.baomidou.mybatisplus.annotation.*;

import java.util.Date;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors; /**
* <p>
* 用户表
* </p>
*
* @author lyh
* @since 2020-06-08
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("back_user")
@ApiModel(value="User对象", description="用户表")
public class User implements Serializable { private static final long serialVersionUID=1L; @ApiModelProperty(value = "用户 ID")
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id; @ApiModelProperty(value = "用户名")
private String name; @ApiModelProperty(value = "用户手机号")
private String mobile; @ApiModelProperty(value = "用户密码")
private String password; @TableField(fill = FieldFill.INSERT)
@ApiModelProperty(value = "创建时间")
private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE)
@ApiModelProperty(value = "修改时间")
private Date updateTime; @TableField(fill = FieldFill.INSERT)
@ApiModelProperty(value = "逻辑删除标志,0 表示未删除, 1 表示删除")
private Integer deleteFlag; @TableField(fill = FieldFill.INSERT)
@ApiModelProperty(value = "版本号")
private Integer version; }

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step3:
  在 TestController.java 中添加测试代码:

package com.lyh.admin_template.back.controller;

import com.lyh.admin_template.back.common.utils.Result;
import com.lyh.admin_template.back.entity.User;
import com.lyh.admin_template.back.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; /**
* 用于测试环境搭建各个功能是否成功
*/
@RestController
@RequestMapping("/test")
@Api(tags = "测试页面")
public class TestController { @Autowired
private UserService userService; @ApiOperation(value = "获取所有用户信息")
@GetMapping("/getListOfUser")
public Result testGetListOfUser() {
return Result.ok().data("user", userService.list());
} @ApiOperation(value = "测试 MyBatis Plus 自动填充数据功能")
@PostMapping("/testMyBatisPlus/AutoFill")
public Result testMyBatisPlusAutoFill(@RequestBody User user) {
if(userService.save(user)) {
return Result.ok().message("数据添加成功");
}
return Result.error().message("数据添加失败");
} }

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step4:
  使用 Swagger,并输入以下数据进行测试:
  由于 id 采用 IdType.ASSIGN_ID 类型,其也是自动生成的。
  所以只需要添加 mobile、name、password 三个值即可。
  且 name 在 back_user 表中是唯一的,所以重复添加时会添加失败。

{
"mobile": "13865561381",
"name": "lyh",
"password": "123456"
}

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

(5)实现逻辑删除
  删除数据时,有物理删除(直接从数据表中删除)、逻辑删除(修改表中某字段,该字段表示该行数据已被删除,但仍留在数据表中,查询数据时此数据不可见)。
可以参考:
  https://www.cnblogs.com/l-y-h/p/12859477.html#_label1_4

Step1:
  在实体类上使用 @TableLogic 注解标注逻辑删除字段,并指定字段变化规则。
  指定 back_user 表中的 delete_flag 字段,0 表示未删除, 1 表示删除。

package com.lyh.admin_template.back.entity;

import com.baomidou.mybatisplus.annotation.*;

import java.util.Date;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors; /**
* <p>
* 用户表
* </p>
*
* @author lyh
* @since 2020-06-08
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("back_user")
@ApiModel(value="User对象", description="用户表")
public class User implements Serializable { private static final long serialVersionUID=1L; @ApiModelProperty(value = "用户 ID")
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id; @ApiModelProperty(value = "用户名")
private String name; @ApiModelProperty(value = "用户手机号")
private String mobile; @ApiModelProperty(value = "用户密码")
private String password; @TableField(fill = FieldFill.INSERT)
@ApiModelProperty(value = "创建时间")
private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE)
@ApiModelProperty(value = "修改时间")
private Date updateTime; @TableField(fill = FieldFill.INSERT)
@TableLogic(value = "0", delval = "1")
@ApiModelProperty(value = "逻辑删除标志,0 表示未删除, 1 表示删除")
private Integer deleteFlag; @TableField(fill = FieldFill.INSERT)
@ApiModelProperty(value = "版本号")
private Integer version; }

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step2:
  编写 TestController.java 进行测试。

package com.lyh.admin_template.back.controller;

import com.lyh.admin_template.back.common.utils.Result;
import com.lyh.admin_template.back.entity.User;
import com.lyh.admin_template.back.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import java.util.HashMap; /**
* 用于测试环境搭建各个功能是否成功
*/
@RestController
@RequestMapping("/test")
@Api(tags = "测试页面")
public class TestController { @Autowired
private UserService userService; @ApiOperation(value = "获取所有用户信息")
@GetMapping("/getListOfUser")
public Result testGetListOfUser() {
return Result.ok().data("user", userService.list());
} @ApiOperation(value = "删除指定姓名的用户")
@DeleteMapping("/testMyBatisPlus/LogicDel")
public Result testMyBatisPlusLogicDel(@RequestParam String name) {
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put("name", name);
userService.removeByMap(hashMap);
return Result.ok();
} @ApiOperation(value = "测试 MyBatis Plus 自动填充数据功能")
@PostMapping("/testMyBatisPlus/AutoFill")
public Result testMyBatisPlusAutoFill(@RequestBody User user) {
if(userService.save(user)) {
return Result.ok().message("数据添加成功");
}
return Result.error().message("数据添加失败");
}
}

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step3:
  测试逻辑删除。
  逻辑删除后,数据仍存于表中,但查询不可见。

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

(6)分页、乐观锁实现
  分页、乐观锁实现需要借助插件来完成了,需要进行额外的配置。
可以参考:
  https://www.cnblogs.com/l-y-h/p/12859477.html#_label1_5
  https://www.cnblogs.com/l-y-h/p/12859477.html#_label1_6
此处直接写实现逻辑,不详细解释。

Step1:
  在 common.config 目录下编写一个配置类 MyBatisPlusConfig.java。
  此时可以将 启动类上的 @MapperScan 移到此处。

package com.lyh.admin_template.back.common.config;

import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; /**
* 自定义一个 MybatisPlus 配置类,配置分页插件、乐观锁插件
* mapper 扫描也可在此写上
*/
@Configuration
@MapperScan("com.lyh.admin_template.back.mapper")
public class MyBatisPlusConfig {
/**
* 分页插件
* @return 分页插件的实例
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
} /**
* 乐观锁插件
* @return 乐观锁插件的实例
*/
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
}

Step2:
  对于实现乐观锁,只需在 实体类字段上标注 @Version 注解即可。
如下,给 User.java 的 version 字段添加 @Version 注解。

package com.lyh.admin_template.back.entity;

import com.baomidou.mybatisplus.annotation.*;

import java.util.Date;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors; /**
* <p>
* 用户表
* </p>
*
* @author lyh
* @since 2020-06-08
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("back_user")
@ApiModel(value="User对象", description="用户表")
public class User implements Serializable { private static final long serialVersionUID=1L; @ApiModelProperty(value = "用户 ID")
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id; @ApiModelProperty(value = "用户名")
private String name; @ApiModelProperty(value = "用户手机号")
private String mobile; @ApiModelProperty(value = "用户密码")
private String password; @TableField(fill = FieldFill.INSERT)
@ApiModelProperty(value = "创建时间")
private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE)
@ApiModelProperty(value = "修改时间")
private Date updateTime; @TableField(fill = FieldFill.INSERT)
@TableLogic(value = "0", delval = "1")
@ApiModelProperty(value = "逻辑删除标志,0 表示未删除, 1 表示删除")
private Integer deleteFlag; @Version
@TableField(fill = FieldFill.INSERT)
@ApiModelProperty(value = "版本号")
private Integer version; }

Step3:
  对于实现分页,首先声明一个 Page 对象,并指定查询的当前页以及每页的大小,然后直接调用 MyBatisPlus 提供的 page 方法即可,page 方法中可以添加过滤条件。
如下,编写 TestController.java 用于测试

package com.lyh.admin_template.back.controller;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.lyh.admin_template.back.common.utils.Result;
import com.lyh.admin_template.back.entity.User;
import com.lyh.admin_template.back.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; /**
* 用于测试环境搭建各个功能是否成功
*/
@RestController
@RequestMapping("/test")
@Api(tags = "测试页面")
public class TestController { @Autowired
private UserService userService; @ApiOperation(value = "测试分页插件")
@GetMapping("/testMyBatisPlus/page/{current}/{size}")
public Result testPage(@PathVariable("current") Long current, @PathVariable("size") Long size) {
Page<User> page = new Page(current, size);
return Result.ok().data("page", userService.page(page, null));
}
}

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

3、SpringBoot 整合 JSR303

(1)简介
  使用 JSR303,采用注解的方式,对传入接口的数据进行数据校验,减少额外的校验代码。
可以参考:
  https://www.cnblogs.com/l-y-h/p/12797809.html

(2)代码实现:
Step1:
  添加相关依赖信息。

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Step2:
  定义两个接口,AddGroup.java、UpdateGroup.java,用于分组校验。

【AddGroup.java】
package com.lyh.admin_template.back.common.validator.group; /**
* 新增数据的 Group 校验规则
*/
public interface AddGroup {
} 【UpdateGroup.java】
package com.lyh.admin_template.back.common.validator.group; /**
* 更新数据时的 Group 校验规则
*/
public interface UpdateGroup {
}

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step3:
  在全局异常处理中,处理检验失败所抛出的异常。

package com.lyh.admin_template.back.common.exception;

import com.lyh.admin_template.back.common.utils.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice; import java.util.HashMap;
import java.util.Map; /**
* 全局异常处理类。
* 使用 slf4j 保存日志信息。
* 此处使用了 统一结果处理 类 Result 用于包装异常信息。
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
private Logger logger = LoggerFactory.getLogger(getClass()); /**
* 处理 Exception 异常
* @param e 异常
* @return 处理结果
*/
@ExceptionHandler(Exception.class)
public Result handlerException(Exception e) {
logger.error(e.getMessage(), e);
return Result.error().message("系统异常");
} /**
* 处理空指针异常
* @param e 异常
* @return 处理结果
*/
@ExceptionHandler(NullPointerException.class)
public Result handlerNullPointerException(NullPointerException e) {
logger.error(e.getMessage(), e);
return Result.error().message("空指针异常");
} /**
* 处理自定义异常
* @param e 异常
* @return 处理结果
*/
@ExceptionHandler(GlobalException.class)
public Result handlerGlobalException(GlobalException e) {
logger.error(e.getMessage(), e);
return Result.error().message(e.getMessage()).code(e.getCode());
} /**
* 处理 JSR303 校验的异常
* @param e 异常
* @return 处理结果
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result handlerValidException(MethodArgumentNotValidException e) {
logger.error(e.getMessage(), e);
BindingResult result = e.getBindingResult();
Map<String, String> map = new HashMap<>();
// 获取校验结果,遍历获取捕获到的每个校验结果
result.getFieldErrors().forEach(item ->{
// 存储得到的校验结果
map.put(item.getField(), item.getDefaultMessage());
});
return Result.error().message("数据校验不合法").data("items", map);
}
}

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step4:
  在实体类上添加相关 JSR 303 注解即可使用。
如下,在 name、mobile、password 字段上添加 注解并指定 分组校验规则。

package com.lyh.admin_template.back.entity;

import com.baomidou.mybatisplus.annotation.*;
import com.lyh.admin_template.back.common.validator.group.AddGroup;
import com.lyh.admin_template.back.common.validator.group.UpdateGroup;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors; import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import java.io.Serializable;
import java.util.Date; /**
* <p>
* 用户表
* </p>
*
* @author lyh
* @since 2020-06-08
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("back_user")
@ApiModel(value="User对象", description="用户表")
public class User implements Serializable { private static final long serialVersionUID=1L; @ApiModelProperty(value = "用户 ID")
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id; @NotEmpty(message = "用户名不能为空", groups = {AddGroup.class, UpdateGroup.class})
@ApiModelProperty(value = "用户名")
private String name; @NotEmpty(message = "手机号不能为空", groups = {AddGroup.class, UpdateGroup.class})
@Pattern(message = "手机号格式不合法", regexp = "^[1-9]{1}\\d{10}$", groups = {AddGroup.class, UpdateGroup.class})
@ApiModelProperty(value = "用户手机号")
private String mobile; @NotEmpty(message = "用户密码不能为空", groups = {AddGroup.class, UpdateGroup.class})
@ApiModelProperty(value = "用户密码")
private String password; @TableField(fill = FieldFill.INSERT)
@ApiModelProperty(value = "创建时间")
private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE)
@ApiModelProperty(value = "修改时间")
private Date updateTime; @TableField(fill = FieldFill.INSERT)
@TableLogic(value = "0", delval = "1")
@ApiModelProperty(value = "逻辑删除标志,0 表示未删除, 1 表示删除")
private Integer deleteFlag; @Version
@TableField(fill = FieldFill.INSERT)
@ApiModelProperty(value = "版本号")
private Integer version; }

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step4:
  编写 TestController.java 用于测试 JSR303 校验 。

package com.lyh.admin_template.back.controller;

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.lyh.admin_template.back.common.utils.Result;
import com.lyh.admin_template.back.common.validator.group.AddGroup;
import com.lyh.admin_template.back.common.validator.group.UpdateGroup;
import com.lyh.admin_template.back.entity.User;
import com.lyh.admin_template.back.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; /**
* 用于测试环境搭建各个功能是否成功
*/
@RestController
@RequestMapping("/test")
@Api(tags = "测试页面")
public class TestController { @Autowired
private UserService userService; @ApiOperation(value = "测试 JSR 303 添加时的校验规则")
@PostMapping("testValidator/save")
public Result testValidatorSave(@Validated({AddGroup.class}) @RequestBody User user) {
if(userService.save(user)) {
return Result.ok().message("数据添加成功");
}
return Result.error().message("数据添加失败");
} @ApiOperation(value = "测试 JSR 303 更新时的校验规则")
@PostMapping("testValidator/update")
public Result testValidatorUpdate(@Validated({UpdateGroup.class}) @RequestBody User user) {
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("name", user.getName());
if(userService.update(user, updateWrapper)) {
return Result.ok().message("数据更新成功");
}
return Result.error().message("数据更新失败");
}
}

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step5:
  使用 Swagger 进行测试。
  由于添加、修改在此处指定的规则均相同,当数据为空或者手机号不合法时,会校验失败,不会进入接口。数据校验通过后,才会执行接口中逻辑。

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

4、SpringBoot 实现热部署(可选操作)

(1)简介
  每次修改代码,都需要重新启动项目,随着项目内容增大,启动耗时会很长,严重影响开发效率。实现热部署,即在项目运行时修改 Java 文件,最后项目内容会随之修改,不用重启项目(当然热部署也会出现一些问题)。

(2)实现
  此处采用 spring-boot-devtools 提供的开发者工具实现热部署。
  其基于类加载机制来实现热部署,即修改完代码后,需要重新编译当前代码才能触发热部署。IDEA 中默认关闭自动编译,需要按下面步骤将其开启。

Step1:
  添加依赖信息。

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>

Step2:
  在 idea 中开启项目自动编译。
  打开设置窗口,找到 Build, Execution, Deployment -> Compiler
  然后勾选 Build project automatically 。

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step3:
  在 idea 中开启项目运行时自动 make。
  按快捷键 ctrl + alt + shift + /,可以弹出一个框,选择 Registry.

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

进入 Registry 窗口后,找到 compiler.automake.allow.when.app.running 并勾选。

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step4:
  开启项目的热部署策略。

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step5:
  修改完后,记得重启项目。然后修改代码,查看是否生效(可能需要等待几秒)。
  快捷键 Ctrl + Shift + F9 可以重新编译项目(有时候热部署可能会删除一些文件,若显示异常,可以手动编译一下)。

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

5、SpringBoot 实现国际化操作

(1)简介
  代码中,需要进行语言切换(比如中英文切换),前端做了相关国际化处理,后端也需要进行类似的处理。否则中文系统下返回英文数据,英文系统下返回中文数据,看起来就很诡异。

(2)实现步骤:
  SpringBoot 内置了国际化处理,可以直接使用,当然也可以自定义使用。
如下为自定义使用步骤。
Step1:在 resources 目录下定义好相关的资源文件,并填入相应的中英文数据信息。
Step2:在项目配置文件中 定义 spring.messages.basename,用于指定资源文件。
Step3:添加一个 国际化的配置文件,用于拦截并处理转换语言。
Step4:添加一个工具类,用于获取国际化的数据。

【自定义国际化操作 参考:】
https://www.jianshu.com/p/a354d3f849ec 【LocaleResolver 使用参考】
https://ethendev.github.io/2017/10/15/SpringBoot-internationalization/ 【WebMvcConfigurer 使用参考:】
https://blog.csdn.net/zhangpower1993/article/details/89016503

(3)代码实现:
Step1:
  在 resources/static 目录下,新建一个 i18n 目录,用于存放国际化资源文件。
  在该目录下新建两个文件:messages_zh_CN.properties、messages_en_US.properties。
  SpringBoot 中配置类 MessageSourceAutoConfiguration.java 源码中默认配置文件前缀为 messages。可以在 SpringBoot 的配置文件中指定 spring.messages.basename,从而自定义文件路径。

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

定义国际化资源文件内容:

【messages_en_US.properties】
test=test I18n 【messages_en_US.properties】
test=测试 I18n

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step2:
  在 application.yml 中定义 spring.messages.basename,用于指定 Step1 中定义的国际化资源文件路径。也可指定 编码字符集。

  有时候会出现中文乱码情况,请自行检查国际化资源文件是否为 UTF-8 编码格式的。

spring:
# 配置国际化,basename 指定国际化文件前缀
messages:
basename: static/i18n/messages
encoding: UTF-8

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step3:
  新建一个国际化配置文件 LocaleConfig.java,用于处理 国际化相关逻辑。
其中:
  LocaleResolver 用于解析当前语言环境。
  LocaleChangeInterceptor 用于获取请求中的语言环境。
  ResourceBundleMessageSource 用于加载国际资源文件位置。

方式一:
  使用 LocaleResolver 并根据 session 去解析当前语言环境。

package com.lyh.admin_template.back.common.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver; import java.util.Locale; /**
* 配置国际化语言
*/
@Configuration
public class LocaleConfig { /**
* 默认解析器
*/
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver localeResolver = new SessionLocaleResolver();
localeResolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
return localeResolver;
} /**
* 定义拦截器,其中 language 表示切换语言的参数名。
*/
@Bean
public WebMvcConfigurer localeInterceptor() {
return new WebMvcConfigurer() {
@Override
public void addInterceptors(InterceptorRegistry registry) {
LocaleChangeInterceptor localeInterceptor = new LocaleChangeInterceptor();
localeInterceptor.setParamName("language");
registry.addInterceptor(localeInterceptor);
}
};
} /**
* 由于返回类型不是 MessageSource 类型,所以需要在 @Bean 注解中指明 name 参数,用于自动装配到 MessageSource 中。
* 使用时 通过 @Autowired 注入 MessageSource 即可。
* setBasenames 用于指定国际化资源文件前缀
* setDefaultEncoding 用于指定编码字符集
*/
@Bean(name = "messageSource")
public ResourceBundleMessageSource getMessageSource() throws Exception {
ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource();
resourceBundleMessageSource.setDefaultEncoding("UTF-8");
resourceBundleMessageSource.setBasenames("static/i18n/messages");
return resourceBundleMessageSource;
}
}

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

方式二(此项目中使用):
  使用 LocaleResolver 并根据 cookie 去解析当前语言环境。

package com.lyh.admin_template.back.common.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; import java.util.Locale; /**
* 配置国际化语言
*/
@Configuration
public class LocaleConfig { /**
* 默认解析器
*/
@Bean
public LocaleResolver localeResolver() {
CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver();
cookieLocaleResolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
cookieLocaleResolver.setCookieName("language");
cookieLocaleResolver.setCookieMaxAge(-1);
return cookieLocaleResolver;
} /**
* 定义拦截器,其中 language 表示切换语言的参数名。
* 拦截请求后,根据请求中的 language 参数去设置语言。
*/
@Bean
public WebMvcConfigurer localeInterceptor() {
return new WebMvcConfigurer() {
@Override
public void addInterceptors(InterceptorRegistry registry) {
LocaleChangeInterceptor localeInterceptor = new LocaleChangeInterceptor();
localeInterceptor.setParamName("language");
registry.addInterceptor(localeInterceptor);
}
};
} /**
* 由于返回类型不是 MessageSource 类型,所以需要在 @Bean 注解中指明 name 参数,用于自动装配到 MessageSource 中。
* 使用时 通过 @Autowired 注入 MessageSource 即可。
* setBasenames 用于指定国际化资源文件前缀
* setDefaultEncoding 用于指定编码字符集
*/
@Bean(name = "messageSource")
public ResourceBundleMessageSource getMessageSource() throws Exception {
ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource();
resourceBundleMessageSource.setDefaultEncoding("UTF-8");
resourceBundleMessageSource.setBasenames("static/i18n/messages");
return resourceBundleMessageSource;
}
}

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step4:
  在 common.utils 目录下定义一个国际化工具类,用于获取国际化文件中的数据。

package com.lyh.admin_template.back.common.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Component; import java.util.Locale; /**
* 国际化工具类,用于获取国际化文件中的数据
*/
@Component
public class MessageSourceUtil { @Autowired
private MessageSource messageSource; public String getMessage(String code) {
return getMessage(code, null);
} public String getMessage(String code, Object[] args) {
return getMessage(code, args, "");
} public String getMessage(String code, Object[] args, String defaultMsg) {
// 获取当前语言信息
Locale locale = LocaleContextHolder.getLocale();
// 根据 code 从国际化资源文件中查找相关信息
return messageSource.getMessage(code, args, defaultMsg, locale);
} }

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step5:
  编写 TestController.java 进行测试。

package com.lyh.admin_template.back.controller;

import com.lyh.admin_template.back.common.utils.MessageSourceUtil;
import com.lyh.admin_template.back.common.utils.Result;
import com.lyh.admin_template.back.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; /**
* 用于测试环境搭建各个功能是否成功
*/
@RestController
@RequestMapping("/test")
@Api(tags = "测试页面")
public class TestController { @Autowired
private UserService userService;
@Autowired
private MessageSourceUtil messageSourceUtil; @ApiOperation(value = "测试国际化返回数据")
@GetMapping("/testLocale")
public Result testI18n() {
return Result.ok().data("test", messageSourceUtil.getMessage("test"));
}
}

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

如下图所示,
  当 cookie 中不存在 language 时,默认返回中文数据。
  当 cookie 中存在 language = zh_CH 时,返回中文数据。
  当 cookie 中存在 language = en_US 时,返回英文数据。

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

(4)整合 JSR303 进行国际化处理

【JSR303 国际化问题 参考:】
https://blog.csdn.net/jin861625788/article/details/73181075

对于 JSR303 国际化问题,只需要让其也加载 国际化资源文件即可。

Step1:
  可以手动配置 Validator 实例对象。
  引入 import javax.validation.Validator; 不要导错包了。

package com.lyh.admin_template.back.common.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; import javax.validation.Validator;
import java.util.Locale; /**
* 配置国际化语言
*/
@Configuration
public class LocaleConfig { /**
* 默认解析器
*/
@Bean
public LocaleResolver localeResolver() {
CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver();
cookieLocaleResolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
cookieLocaleResolver.setCookieName("language");
cookieLocaleResolver.setCookieMaxAge(-1);
return cookieLocaleResolver;
} /**
* 定义拦截器,其中 language 表示切换语言的参数名。
* 拦截请求后,根据请求中的 language 参数去设置语言。
*/
@Bean
public WebMvcConfigurer localeInterceptor() {
return new WebMvcConfigurer() {
@Override
public void addInterceptors(InterceptorRegistry registry) {
LocaleChangeInterceptor localeInterceptor = new LocaleChangeInterceptor();
localeInterceptor.setParamName("language");
registry.addInterceptor(localeInterceptor);
}
};
} /**
* 由于返回类型不是 MessageSource 类型,所以需要在 @Bean 注解中指明 name 参数,用于自动装配到 MessageSource 中。
* 使用时 通过 @Autowired 注入 MessageSource 即可。
* setBasenames 用于指定国际化资源文件前缀
* setDefaultEncoding 用于指定编码字符集
*/
@Bean(name = "messageSource")
public ResourceBundleMessageSource getMessageSource() throws Exception {
ResourceBundleMessageSource resourceBundleMessageSource = new ResourceBundleMessageSource();
resourceBundleMessageSource.setDefaultEncoding("UTF-8");
resourceBundleMessageSource.setBasenames("static/i18n/messages");
return resourceBundleMessageSource;
} /**
* 配置 JSR303 国际化问题,让其加载国际化资源文件
* @return
* @throws Exception
*/
@Bean
public Validator getValidator() throws Exception {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setValidationMessageSource(getMessageSource());
return validator;
} }

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step2:
  修改相关实体类,使用 {} 获取配置文件中的值。

package com.lyh.admin_template.back.entity;

import com.baomidou.mybatisplus.annotation.*;
import com.lyh.admin_template.back.common.validator.group.AddGroup;
import com.lyh.admin_template.back.common.validator.group.UpdateGroup;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors; import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import java.io.Serializable;
import java.util.Date; /**
* <p>
* 用户表
* </p>
*
* @author lyh
* @since 2020-06-08
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("back_user")
@ApiModel(value="User对象", description="用户表")
public class User implements Serializable { private static final long serialVersionUID=1L; @ApiModelProperty(value = "用户 ID")
@TableId(value = "id", type = IdType.ASSIGN_ID)
private Long id; @NotEmpty(message = "{user.name.empty}", groups = {AddGroup.class, UpdateGroup.class})
@ApiModelProperty(value = "用户名")
private String name; @NotEmpty(message = "{user.mobile.empty}", groups = {AddGroup.class, UpdateGroup.class})
@Pattern(message = "手机号格式不合法", regexp = "^[1-9]{1}\\d{10}$", groups = {AddGroup.class, UpdateGroup.class})
@ApiModelProperty(value = "用户手机号")
private String mobile; @NotEmpty(message = "{user.password.empty}", groups = {AddGroup.class, UpdateGroup.class})
@ApiModelProperty(value = "用户密码")
private String password; @TableField(fill = FieldFill.INSERT)
@ApiModelProperty(value = "创建时间")
private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE)
@ApiModelProperty(value = "修改时间")
private Date updateTime; @TableField(fill = FieldFill.INSERT)
@TableLogic(value = "0", delval = "1")
@ApiModelProperty(value = "逻辑删除标志,0 表示未删除, 1 表示删除")
private Integer deleteFlag; @Version
@TableField(fill = FieldFill.INSERT)
@ApiModelProperty(value = "版本号")
private Integer version; }

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step3:
  修改对应的资源文件。

【messages_en_US.properties】
user.name.empty=User name cannot be null
user.mobile.empty=User mobile cannot be null
user.password.empty=User password cannot be null 【messages_zh_CH.properties】
user.name.empty=用户名不能为空
user.mobile.empty=用户手机号不能为空
user.password.empty=用户密码不能为空

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作

Step4:
  简单测试一下。
  使用之前写好的 “测试 JSR 303 添加时的校验规则” 接口用于测试。

SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后端篇(一): 搭建基本环境、整合 Swagger、MyBatisPlus、JSR303 以及国际化操作