一、项目描述
一个简单的学生信息管理系统,主要用来练习前后端分离下的权限管理
学生可以查看自己所在班级的学生信息,修改自己的信息
老师可以查看自己管理班级的学生信息,能增加、修改学生信息
管理员可以查看所有班级的老师和学生信息,并可以增删改
二、所用技术
前端:vue(cli)、elementui、axios
后端:springboot、mybatisplus、springsecurity、jwt、easyexcel
数据库:mysql、redis
三、前后端协同点
对axios进行请求拦截器封装,每次请求携带token,后端根据token进行鉴权
对axios进行响应拦截器封装,后端进行全局异常处理时,前端展示相应提示
前端负责页面跳转管理,使用路由管理页面访问路径,无权限不能进入对应页面;后端只负责api鉴权,不涉及页面管理
四、数据库设计
一共四张表:学生表,班级表,教师表,教师_班级表,表之间使用逻辑外键、不建立物理外键关联。
管理员默认admin,密码123456,不再建表。学生和班级表初始信息使用excle导入,教师信息由管理员手动添加
表之间关系:
班级 —— 学生 一对多(在学生表中加入班级id作为外键)
班级 —— 教师 多对多(使用中间表,即教师_班级表)
创建数据库sims,建表sql语句如下
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for clazz
-- ----------------------------
DROP TABLE IF EXISTS `clazz`;
CREATE TABLE `clazz` (
`id` char(19) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '班级id',
`name` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '班级名称',
`gmt_create` datetime(0) NOT NULL COMMENT '创建时间',
`gmt_modified` datetime(0) NOT NULL COMMENT '更新时间',
`deleted` tinyint(1) NULL DEFAULT 0 COMMENT '逻辑删除,默认为0表示未删,1为已删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` char(19) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '学生id',
`sno` char(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '学号,11位',
`name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '学生姓名',
`password` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '学生密码',
`address` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '家庭住址',
`email` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '邮箱',
`clazz_id` char(19) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '所属班级id',
`gmt_create` datetime(0) NOT NULL COMMENT '创建时间',
`gmt_modified` datetime(0) NOT NULL COMMENT '更新时间',
`deleted` tinyint(1) NULL DEFAULT 0 COMMENT '逻辑删除,默认为0表示未删,1为已删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for teacher
-- ----------------------------
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (
`id` char(19) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '教师id',
`name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '教师姓名',
`tno` int(0) NOT NULL COMMENT '教师工号',
`gmt_create` datetime(0) NOT NULL COMMENT '创建时间',
`gmt_modified` datetime(0) NOT NULL COMMENT '更新时间',
`deleted` tinyint(1) NULL DEFAULT 0 COMMENT '逻辑删除,默认为0表示未删,1为已删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for teacher_clazz
-- ----------------------------
DROP TABLE IF EXISTS `teacher_clazz`;
CREATE TABLE `teacher_clazz` (
`id` char(19) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '教师班级表id',
`teacher_id` char(19) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '教师id',
`clazz_id` char(19) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '班级id',
`gmt_create` datetime(0) NOT NULL COMMENT '创建时间',
`gmt_modified` datetime(0) NOT NULL COMMENT '更新时间',
`deleted` tinyint(1) NULL DEFAULT 0 COMMENT '逻辑删除,默认为0表示未删,1为已删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
五、创建前后端项目、搭建基础框架
这一步只是把项目简单搭建起来,并做好基础配置,如前端axios封装,后端生成代码、swagger配置、全局异常处理等
(一)、创建前端项目
1、创建项目
//创建项目 创建的时候选择使用vue-router,之后不再安装
vue init webpack sims
//安装依赖
cnpm i element-ui qs axios -S
2、引入依赖并调整项目结构
在main.js中引入elementui。axios和qs之后我们会在封装的时候用到,封装后只在api中使用用,就不在这里引入了
import Vue from 'vue'
import App from './App'
import router from './router'
//引入并声明elementui
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
})
整个项目的目录结构如下,我们需要新建的有api、utils、views文件夹
现在项目默认的是components文件夹里的HelloWorld.vue为主页,先把它改为我们自定义的
- 删除HelloWorld.vue文件,并在views下新建index.vue
- 修改router下的index.js文件,引入index.vue并设置路由
import Vue from 'vue'
import Router from 'vue-router'
import index from '@/views/index'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'index',
component: index
}
]
})
- 修改App.vue,去除logo和样式
<template>
<div id="app">
<router-view/>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
执行命令npm run dev启动项目,效果如下
接下来在utils文件夹下新建request.js对axios进行封装,代码如下
import axios from 'axios';
import qs from 'qs';
//请求超时时间为10s
axios.defaults.timeout = 10000;
//允许跨域携带资源凭证
axios.defaults.withCredentials = true;
//设置请求头,如果后端要接收json格式,则改为application/json
axios.defaults.headers['Content-Type'] = 'application/x-www-form-urlencoded';
//使用qs格式化数据,若为json则格式化为json
axios.defaults.transformRequest = data => qs.stringify(data);
//设置请求拦截器,每次请求在请求头里加上token
axios.interceptors.request.use(config => {
const token = localStorage.getItem('token');
token && (config.Authorization = token);
return config;
},error => {
return Promise.reject(error);
});
//设置响应拦截器
axios.interceptors.response.use(response => {
//这里也可以拿到后台传过来data里面自定义的code码,以确定是否成功及后续操作
return response.data;
},error => {
//根据http状态码进行相应的错误处理
if(error.response){
switch(error.response.status){
case 401:
break;
case 403:
break;
case 404:
break;
}
}else{
//若用户网络断开,执行的操作
if(!window.navigator.onLine){
return;
}
return Promise.reject(error);
}
});
export default request;
至此,简单的前端基础项目搭建完成,页面逻辑我们之后再写
(二)、创建后端项目
1、创建项目
新建springboot项目,名称为sims,包名为com.sims。项目结构如下
2、添加依赖
pom文件如下,项目比较简单,不再划分子模块
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>sims</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>sims</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<!--代码生成器依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<!-- velocity 模板引擎,用于代码生成 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.2</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--lombok,idea需安装插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--knife4j中已经引入了swagger,不用再单独引入-->
<!--<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>-->
<!--knife4j增强swagger-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
3、修改配置文件
springboot默认生成的是properties文件,这里改成了yml文件。
# 服务端口
server:
port: 8081
spring:
# 环境设置:dev、test、prod
profiles:
active: dev
# mysql数据库连接
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/sims?serverTimezone=GMT%2B8
username: root
password: 123456
#mybatis-plus逻辑删除,实际开发中根据需求选择是否使用
mybatis-plus:
global-config:
db-config:
logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
4、使用代码生成器生成代码
@Test
public void run() {
// 1、创建代码生成器
AutoGenerator mpg = new AutoGenerator();
// 2、全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir("E:\\ideaProject\\sims" + "/src/main/java");
gc.setAuthor("leaf");
gc.setOpen(false); //生成后是否打开资源管理器
gc.setFileOverride(false); //重新生成时文件是否覆盖
//UserServie
gc.setServiceName("%sService"); //去掉Service接口的首字母I
gc.setIdType(IdType.ID_WORKER_STR); //主键策略
gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型
gc.setSwagger2(true);//开启Swagger2模式
mpg.setGlobalConfig(gc);
// 3、数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/sims?serverTimezone=GMT%2B8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
// 4、包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName(""); //模块名
pc.setParent("com.sims");
pc.setController("controller");
pc.setEntity("entity");
pc.setService("service");
pc.setMapper("mapper");
mpg.setPackageInfo(pc);
// 5、策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("student","teacher","clazz","teacher_clazz");
strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
strategy.setTablePrefix(pc.getModuleName() + "_"); //生成实体时去掉表前缀
strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作
strategy.setRestControllerStyle(true); //restful api风格控制器
strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符
mpg.setStrategy(strategy);
// 6、执行
mpg.execute();
}
将以上代码复制到springboot项目自带的test类中运行即可,需要注意修改gc.setOutputDir里的路径为项目的磁盘路径,若数据库连接、用户名、密码不一致也需要修改,其他设置根据需求自行修改。
生成完毕后项目目录如下
5、完善项目配置
(1)、mybatis-plus配置
在com.sims下新建包config,在config包下新建类MybatisPlusConfig ,代码如下
@Configuration
@MapperScan("com.sims.mapper")
public class MybatisPlusConfig {
//分页插件
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
}
由于每张表里都有gmt_create和gmt_modified字段,每次添加或修改都要进行操作,因此使用处理器实现自动填充
新建包handler,在handler下新建类MyMetaObjectHandler并实现MetaObjectHandler接口
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
//插入时,创建时间和更新时间一致
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("gmtCreate",new Date(),metaObject);
this.setFieldValByName("gmtModified",new Date(),metaObject);
}
//修改时时只修改更新时间
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("gmtModified",new Date(),metaObject);
}
}
除此之外还需要在每个实体类中的的gmtCreate和gmtModified字段上添加@TableField注解使handler生效
@ApiModelProperty(value = "创建时间")
@TableField(fill = FieldFill.INSERT)
private Date gmtCreate;
@ApiModelProperty(value = "更新时间")
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date gmtModified;
(2)、swagger配置
在pom文件中我们已经引入了增强swagger的knife4j,接下来只需要进行简单的配置就可以使用了
在config包下新建类SwaggerConfig,代码如下
package com.sims.config;
@Configuration
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.sims.controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("学生信息管理系统")
.version("1.0")
.build();
}
}
(3)、统一结果返回
新建包utils,在utils下新建Result类,用于向前端返回统一数据,这里只是简单分类,实际开发中可以根据业务将状态码详细划分
@Data
public class Result<T> {
public static Integer SUCCESS = 1; //成功
public static Integer ERROR = 0; //失败
@ApiModelProperty(value = "状态码")
private Integer code;
@ApiModelProperty(value = "提示信息")
private String message;
@ApiModelProperty(value = "数据")
private T data;
private Result() {}
public static Result success() {
Result result = new Result();
result.setCode(SUCCESS);
result.setMessage("操作成功");
return result;
}
public static Result error() {
Result result = new Result();
result.setCode(ERROR);
result.setMessage("操作失败");
return result;
}
public Result code(Integer code) {
this.setCode(code);
return this;
}
public Result message(String message) {
this.setMessage(message);
return this;
}
public Result data(T data) {
this.setData(data);
return this;
}
}
(4)、日志处理
在resources目录下新建logback-spring.xml文件
其中需要修改的有
<property name="log.path" value="E:/sims/log"/>,将其中的value值修改为想输出的文件路径
<logger name="com.sims" level="INFO" />,将其中的name值改为当前项目的包路径
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="10 seconds">
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<contextName>logback</contextName>
<!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->
<property name="log.path" value="E:/sims/log" />
<!-- 彩色日志 -->
<!-- 配置格式变量:CONSOLE_LOG_PATTERN 彩色日志格式 -->
<!-- magenta:洋红 -->
<!-- boldMagenta:粗红-->
<!-- cyan:青色 -->
<!-- white:白色 -->
<!-- magenta:洋红 -->
<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)"/>
<!--输出到控制台-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
<!-- 例如:如果此处配置了INFO级别,则后面其他位置即使配置了DEBUG级别的日志,也不会被输出 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<encoder>
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!--输出到文件-->
<!-- 时间滚动输出 level为 INFO 日志 -->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_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/log-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>
<!-- 时间滚动输出 level为 WARN 日志 -->
<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_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/log-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>
<!-- 时间滚动输出 level为 ERROR 日志 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/log_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/log-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>
<!--
<logger>用来设置某一个包或者具体的某一个类的日志打印级别、以及指定<appender>。
<logger>仅有一个name属性,
一个可选的level和一个可选的addtivity属性。
name:用来指定受此logger约束的某一个包或者具体的某一个类。
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,
如果未设置此属性,那么当前logger将会继承上级的级别。
-->
<!--
使用mybatis的时候,sql语句是debug下才会打印,而这里我们只配置了info,所以想要查看sql语句的话,有以下两种操作:
第一种把<root level="INFO">改成<root level="DEBUG">这样就会打印sql,不过这样日志那边会出现很多其他消息
第二种就是单独给mapper下目录配置DEBUG模式,代码如下,这样配置sql语句会打印,其他还是正常DEBUG级别:
-->
<!--开发环境:打印控制台-->
<springProfile name="dev">
<!--可以输出项目中的debug日志,包括mybatis的sql日志-->
<logger name="com.sims" level="INFO" />
<!--
root节点是必选节点,用来指定最基础的日志输出级别,只有一个level属性
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,默认是DEBUG
可以包含零个或多个appender元素。
-->
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="WARN_FILE" />
<appender-ref ref="ERROR_FILE" />
</root>
</springProfile>
<!--生产环境:输出到文件-->
<springProfile name="pro">
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="DEBUG_FILE" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="ERROR_FILE" />
<appender-ref ref="WARN_FILE" />
</root>
</springProfile>
</configuration>
在utils包下新建类ExceptionUtil,用于打印完整日志信息
public class ExceptionUtil {
public static String getMessage(Exception e) {
StringWriter sw = null;
PrintWriter pw = null;
try {
sw = new StringWriter();
pw = new PrintWriter(sw);
// 将出错的栈信息输出到printWriter中
e.printStackTrace(pw);
pw.flush();
sw.flush();
} finally {
if (sw != null) {
try {
sw.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
if (pw != null) {
pw.close();
}
}
return sw.toString();
}
}
(5)、全局异常处理
在handler包下新建全局异常处理类GlobalExceptionHandler和MyException自定义异常类
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
//所有异常
@ExceptionHandler(Exception.class)
@ResponseBody
public Result error(Exception e){
e.printStackTrace();
log.error(ExceptionUtil.getMessage(e));
return Result.error().message("服务器错误");
}
//自定义异常
@ExceptionHandler(MyException.class)
@ResponseBody
public Result error(MyException myException){
myException.printStackTrace();
log.error(ExceptionUtil.getMessage(myException));
return Result.error().code(myException.getCode()).message(myException.getMsg());
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MyException extends RuntimeException{
private Integer code;
private String msg;
}
之后手动抛出自定义异常并传入异常信息即可
6、测试
至此,项目的基础配置已经完成,其他的一些配置需要结合逻辑代码实现,我们之后进行。
现在,先测试一下配置是否正确,能否跑通
- 在StudentController下添加增删改查四个方法(只做测试用,之后还会进行优化修改)
@RestController
@RequestMapping("/student")
public class StudentController {
@Autowired
private StudentService studentService;
@PostMapping
public Result insert(Student student){
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.eq("sno", student.getSno());
Student s = studentService.getOne(queryWrapper);
if(s == null){
studentService.save(student);
}else {
throw new MyException(1, "学号已存在,无法添加");
}
return Result.success().message("添加成功");
}
@GetMapping
public Result getList(){
return Result.success().data(studentService.list());
}
@DeleteMapping("{id}")
public Result delete(@PathVariable String id){
studentService.removeById(id);
return Result.success();
}
@PutMapping
public Result update(Student student){
studentService.update(student,null);
return Result.success();
}
}
- 启动项目,访问http://localhost:8081/doc.html,成功进入swagger页面则swagger配置无问题
这里先删除掉图中的四个选项,正常情况下前端是不会传的,由后端处理,为空会报错。
点击发送,响应信息为成功且为同一返回结果格式,则数据库配置正常,统一返回处理类生效
再次点击发送,响应信息为自定义异常信息,自定义异常处理器生效
找到之前在日志处理配置xml中配置的路径,打开log_error.log日志文件,里面有错误信息,日志处理正常