一、IDEA 插件使用

1、说明

  此处使用 SpringBoot 2.2.6 、JDK 1.8 、mysql 8.0.18 作为演示。

  使用 IDEA 作为开发工具。

2、IDEA 插件 -- Lombok

(1)简介
  Lombok 能通过注解的方式,在编译时自动为属性生成构造器、getter/setter、equals、hashcode、toString 等方法。
  比如在实体类上使用 @Data 注解,就可以省去 getter、 setter 等方法的编写,但是在编译生成的字节码文件中有getter和setter方法。

(2)安装

  Settings -> Plugins,搜索 Lombok。点击 install 安装后重启 IDEA 即可。

(3)使用

Step1:
  使用 maven 添加依赖,引入对应的 jar 包。

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>

Step2:
  使用各种注解,简化代码编写。比如 @Data。

3、IDEA 插件 -- Easy Code

(1)简介
  Easy Code 是 IDEA 的一个插件,可以直接对数据的表生成 entity, controller, service, dao, mapper 等代码,无需任何编码,简单而强大。

(2)安装

  Settings -> Plugins,搜索 EasyCode。点击 install 安装后重启 IDEA 即可。

(3)使用
Step1:
  创建一个 SpringBoot 项目。

Step2:
  连接数据库,并创建一个 数据表,用于测试。

DROP DATABASE IF EXISTS test;

CREATE DATABASE test;

USE test;

CREATE TABLE emp(
id int PRIMARY KEY auto_increment,
name varchar(50),
salary decimal(10, 2),
age int,
email varchar(50)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; INSERT INTO emp VALUES(null, "tom", 6000, 20, "tom@163.com");

Step3:
  右键表名,选择 EasyCode -> Generate Code,生成代码。

选择代码生成的路径 以及 包名。

自动创建需要的文件。

Step4:
  当然这样并不能直接使用,还得添加相关依赖、注解、以及数据库连接信息才能使用。
添加依赖:
  比如: lombok、mybatis-plus、mysql-connector-java 以及 web 启动类。

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency> <!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>

添加注解:
  dao 层添加 @Mapper 注解,或者在启动类上添加 @MapperScan 注解扫描 dao 层。
  entity 上可以添加 @Data 注解,用于简化 getter、setter 方法。

配置 yml 文件:

# 配置数据源
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
url: jdbc:mysql://localhost:3306/test # 配置 mybatis-plus
mybatis-plus:
mapper-location: classpath:/mapper/**/*.xml
global-config:
db-config:
id-type: auto

Step5:启动项目,并访问 controller

4、使用 mybatis-plus 代码生成器生成代码

mybatis-plus 代码生成器:
https://www.cnblogs.com/l-y-h/p/12859477.html#_label1_2

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

1、为什么使用统一结果?

  大部分前后端项目采用 JSON 格式进行数据交互,定义一个统一的数据规范,有利于前后台的交互、以及信息处理。

2、数据格式?如何处理?代码实现?

(1)数据格式?
  是否响应成功(success: true / false)
  响应状态码(code:200 / 400 / 500 等)
  状态码描述(message:访问成功 / 系统异常等)
  响应数据(data:处理的数据)

【输出格式如下所示:】

{
"success": true,
"code": 200,
"message": "查询用户列表",
"data": {
"itms": [
{
"id": "1",
"username": "admin",
"role": "ADMIN",
"createTime": "2020-4-24T15:32:29",
"modifiedTime": "2020-4-24T15:41:40"
},{
"id": "2",
"username": "zhangsan",
"role": "USER",
"createTime": "2020-4-24T15:32:29",
"modifiedTime": "2020-4-24T15:41:40"
}
]
}
}

(2)如何处理?
  success 设置成 Boolean 类型。
  code 设置成 Integer 类型。  
  message 设置成 String 类型。
  data 设置成 HashMap 类型。
  构造器私有,且使用静态方法返回类对象。
  采用链式调用(即方法返回对象为其本身,return this)。

(3)代码实现?
  Step1:创建一个 统一结果处理类 Result 。

【代码实现如下:】

package com.lyh.common.util;

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;
}
}

Step2:依赖信息

<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>

Step3:HttpStatus 状态码参考地址

http://hc.apache.org/httpcomponents-core-ga/httpcore/apidocs/org/apache/http/HttpStatus.html

(4)使用:
  修改某个 controller 如下所示:
  若查询用户成功,调用 ok 返回查询到的结果。
  若查询用户失败,调用 error 返回查询失败信息。

@GetMapping("selectOne")
public Result selectOne(Integer id) {
Emp emp = this.empService.queryById(id);
if (emp == null) {
return Result.error().message("用户不存在");
}
return Result.ok().data("items", emp).message("查询成功");
}

启动服务并访问:
  测试查询用户不成功的返回结果:

{
"success": false,
"code": 500,
"message": "用户不存在",
"data": {}
}

  测试查询用户成功的返回结果:

{
"success": true,
"code": 200,
"message": "查询成功",
"data": {
"items": {
"id": 1,
"name": "tom",
"salary": 6000.0,
"age": 20,
"email": "tom@163.com"
}
}
}

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

1、为什么使用统一异常处理?

  使用统一结果处理时,有些异常我们可以提前预知并处理,但是一个运行时异常,我们不一定能预知并处理,这时可以使用统一异常处理,当异常发生时,触发该处理操作,从而保证程序的健壮性。

2、怎么做?如何处理?代码实现?

(1)怎么做?
  使用 @ControllerAdvice 或者 @RestControllerAdvice 注解作为统一异常处理的核心。
  这两个注解都是 Spring MVC 提供的。作用于 控制层 的一种切面通知。
功能:
  全局异常处理。
  全局数据绑定。
  全局数据预处理。

【@ControllerAdvice 与 @RestControllerAdvice 区别:】
@RestControllerAdvice 注解包含了 @ControllerAdvice 与 @ResponseBody 注解。
类似于 @Controller 与 @RestController 的区别。
@RestControllerAdvice 返回 json 数据时不需要添加 @ResponseBody 注解。

(2)如何处理?
  使用 @ControllerAdvice 或者 @RestControllerAdvice 注解标记一个 全局异常处理类。
  全局异常处理类内部使用 @ExceptionHandler 注解去捕获异常。
  可以自定义一个异常信息收集类,用于处理项目中的异常,并收集异常信息。

(3)代码实现?
  Step1(可选操作):
    自定义一个异常类,用于处理项目中的异常,并收集异常信息。

【代码实现:】

package com.lyh.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;
}
}

Step2:
  定义一个全局的异常处理类 GlobalExceptionHandler。
  使用 @RestControllerAdvice 注解标记这个类。
  内部使用 @ExceptionHandler 注解去捕获异常。

【代码实现:】

package com.lyh.common.exception;

import com.lyh.common.util.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());
}
}

(4)使用?
  修改某个 controller 如下所示:
  参数不存在时,抛出 空指针异常。
  参数为 -1 时,抛出自定义异常并处理。
  查询结果为 null 时,抛出自定义异常并处理。
  查询成功时,正确处理并返回。

@GetMapping("selectOne")
public Result selectOne(Integer id) {
Emp emp = this.empService.queryById(id);
if (id == null) {
throw new NullPointerException();
}
if (id == -1) {
throw new GlobalException("参数异常", 400);
}
if (emp == null) {
throw new GlobalException("未查询到结果,请确认输入是否正确");
}
return Result.ok().data("items", emp).message("查询成功");
}

启动服务并访问:
  参数不存在时:

{
"success": false,
"code": 500,
"message": "空指针异常",
"data": {}
}

  参数存在,但为 -1 时:

{
"success": false,
"code": 400,
"message": "参数异常",
"data": {}
}

  参数存在,但查询结果为 null 时:

{
"success": false,
"code": 500,
"message": "未查询到结果,请确认输入是否正确",
"data": {}
}

  参数存在,查询结果存在时:

{
"success": true,
"code": 200,
"message": "查询成功",
"data": {
"items": {
"id": 1,
"name": "tom",
"salary": 6000.0,
"age": 20,
"email": "tom@163.com"
}
}
}

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

1、为什么使用统一日志处理?

  实际开发中,经常遇到各种各样的问题,使用日志可以方便、快速定位到错误处,便于程序调试。

2、简单介绍?如何处理?实现?

(1)简单介绍
  此处使用 logback 进行日志处理(是 SpringBoot 内置日志框架)。其与 log4j 类似。
  日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出

(2)如何处理?
  一般通过 logback-spring.xml (放在 resources 目录下)作为 日志的配置文件。
  默认情况下,日志输出到控制台,若想输出到文件中,可以简单的配置一下。

简单了解一下 logback-spring.xml 的常用标签信息。

【configuration】
<configuration> </configuration> 是 logback-spring.xml 最外层标签(基本结构),
其内部包含
<contextName></contextName> 用于定义 logger 上下文名称,
<property></property> 用于定义变量值,通过 ${} 引用变量,
<appender></appender> 用于格式化日志并指定输出位置(控制台、文件),
<springProfile></springProfile> 用于定义不同环境的日志(test、dev、prod),
<root></root> 用于定义基本的日志输出级别。
<logger></logger> 用于定义某个包、或类具体的日志输出级别。
等标签。

Step1:了解一下 <property></property> 标签
  property 用于定义变量值,可以通过 ${} 引用变量。
其中:
  name 用于定义变量名。
  value 用于定义变量值。
如下例:
  log.path 用于定义文件输出路径。
  CONSOLE_LOG_PATTERN 用于定义文件输出格式。
  使用 ${log.path }、${CONSOLE_LOG_PATTERN} 即可获取到变量值。

<property name="log.path" value="E:/myProject/log" />
<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)"/>

Step2:了解一下 <appender></appender> 标签
  appender 用于格式化日志并指定输出位置(控制台、文件)。
其中:
  ConsoleAppender 用于输出到控制台。
  RollingFileAppender 用于输出到文件。
如下例:
  日志输出到控制台,
  name 属性用于指定 appender 名,后续可以通过 <appender-ref> 引用。
  class 属性用于指定日志生成策略, ConsoleAppender 为控制台输出策略。
  filter 标签用于过滤日志输出,如下,过滤掉低于 debug 级别的日志。
  encoder 用于定义日志的编码格式,可以设置输出样式、字符集等。

<!--1. 输出到控制台-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
<encoder>
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>

如下例:
  日志输出到文件。
  RollingFileAppender 为文件输出策略。
  file 标签用于指定文件输出路径。
  rollingPolicy 标签用于日志的处理(归档、切割、过期时间等)。

<!-- 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}/web-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:了解一下 <root></root> 标签
  root 用于定义基本的日志输出级别。
其中:
  level 属性用于指定日志输出级别。
  appender-ref 用于引用 <appender> 标签定义的内容。

<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="INFO_FILE" />
</root>

Step4:了解一下 <logger></logger> 标签
  用于定义某个包、或类具体的日志输出级别。
其中:
  name 属性用于指定 某个包或者具体的某个类。
  level 属性用于定义日志输出级别。

<logger name="com.lyh.test" level="INFO" />

Step5:了解一下 <springProfile></springProfile> 标签
  用于定义不同环境下的日志输出。
其中:
  name 属性用于指定环境(test、dev、prod)。
  SpringBoot 中可以通过 yml 或者 properties 指定环境:

spring:
profiles:
active: dev

如下例:
  为开发环境以及生产环境的不同日志输出处理方式。
  开发环境(dev)下,可以在控制台输出。
  生产环境(prod)下,只能输出到文件。

 <!--开发环境:打印控制台-->
<springProfile name="dev">
<logger name="com.lyh.test" level="INFO" />
<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="prod">
<root level="INFO">
<appender-ref ref="ERROR_FILE" />
<appender-ref ref="WARN_FILE" />
</root>
</springProfile>

(3)实现?
Step1:
  logback-spring.xml 完整配置如下(需要做些适当修改)。

<?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.test.test_mybatis_plus" 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>

需要修改的地方如下,其余的根据项目情况进行修改。

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

Step2:
  一般日志会与异常处理相结合。
  前面已经介绍了全局异常处理,在异常处理类中进行相关日志处理,即可。

【全局异常处理:】
https://www.cnblogs.com/l-y-h/p/12781586.html#_label2

  可以自定义一个异常信息处理工具类(可选操作),将堆栈信息转为 String 类型。

【可以自定义一个异常信息处理工具类:(可选操作,ExceptionUtil)】

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;
}
}

修改统一异常处理工具类(GlobalExceptionHandler)。
使用 @Slf4j 标注类。
使用 log.error() 打印日志信息。

import com.lyh.test.test_mybatis_plus.util.Result;
import lombok.extern.slf4j.Slf4j;
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
@Slf4j
public class GlobalExceptionHandler {
/**
* 处理 Exception 异常
* @param e 异常
* @return 处理结果
*/
@ExceptionHandler(Exception.class)
public Result handlerException(Exception e) {
log.error(e.getMessage());
return Result.error().message("系统异常");
} /**
* 处理空指针异常
* @param e 异常
* @return 处理结果
*/
@ExceptionHandler(NullPointerException.class)
public Result handlerNullPointerException(NullPointerException e) {
log.error(e.getMessage());
return Result.error().message("空指针异常");
} /**
* 处理自定义异常
* @param e 异常
* @return 处理结果
*/
@ExceptionHandler(GlobalException.class)
public Result handlerGlobalException(GlobalException e) {
log.error(e.getMessage());
return Result.error().message(e.getMessage()).code(e.getCode());
}
}

简单测试一下:
  如下,抛出自定义异常时,会被 GlobalExceptionHandler 捕获并处理。

@GetMapping("/test")
public Result test() {
try{
System.out.println(1/0);
} catch (Exception e) {
throw new GlobalException(ExceptionUtil.getMessage(e));
}
return Result.ok().data("items", userService.list());
}

Java 项目创建 -- 统一结果处理、统一异常处理、统一日志处理的更多相关文章

  1. mc01_IntelliJ IDEA安装与Java项目创建以及Tomcat配置

    IntelliJ IDEA安装与激活 下载地址:http://www.jetbrains.com/idea/ 安装下一步下一步即可,关键是注册激活,该部分分两个步骤: 1. 配置windows hos ...

  2. java项目创建和部署

    http://www.cnblogs.com/nexiyi/archive/2012/12/28/2837560.html http://dead-knight.iteye.com/blog/1841 ...

  3. IDEA中Java项目创建lib目录并生成依赖

    首先介绍说明一下idea在创建普通的Java项目,是没有lib文件夹的,下面我来带大家来创建一下1.右键点击项目,创建一个普通的文件夹 2.取名为lib 3.把项目所需的jar包复制到lib文件夹下 ...

  4. java 项目创建中的问题汇总

    eclipse 创建maven项目时 报错 Unable to create project from archetype [org.apache.maven.archetypes:ma 你看看你ma ...

  5. VS Code Java 2 月更新!教育特别版:单元测试、GUI开发支持、Gradle项目创建、以及更多!

    新春快乐!欢迎来到 Visual Studio Code Java 的 2 月更新,这个月我们给大家带来了一期教育特别版.每年的年初是许多学校开学的时间,为了给学生和教师提供在 Visual Stud ...

  6. 通过Jenkins部署java项目

    部署java项目-创建私有仓库 Jenkins大多数情况下都是用来部署Java项目,Java项目有一个特点是需要编译和打包的,一般情况下编译和打包都是用maven完成,所以系统环境中需要安装maven ...

  7. Java项目常用的统一返回跟统一异常处理

    先创建一个crud的项目. controller调用service调用mapper 以下以简单代码代替 controller @GetMapping("/getUserById") ...

  8. [原创]Java项目统一UTC时间方案

    Java项目统一UTC时间方案 作者:Gods_巨蚁 引言 近期团队的个别项目在进行框架升级后,部分时间值存在8小时误差,原因是错误的将数据库中的时间数据理解成了UTC时间(旧版本认为是北京时间) 考 ...

  9. Scala IDEA for Eclipse里用maven来创建scala和java项目代码环境(图文详解)

    这篇博客 是在Scala IDEA for Eclipse里手动创建scala代码编写环境. Scala IDE for Eclipse的下载.安装和WordCount的初步使用(本地模式和集群模式) ...

随机推荐

  1. 温故知新-多线程-深入刨析park、unpark

    文章目录 摘要 park.unpark 看一下hotspot实现 参考 你的鼓励也是我创作的动力 Posted by 微博@Yangsc_o 原创文章,版权声明:自由转载-非商用-非衍生-保持署名 | ...

  2. Linux RTC设备驱动

    1. 在Linux2.6.29内核中,RTC是以平台设备的方式注册进内核的. ① RTC驱动定义于文件:drivers/rtc/rtc-s3c.c static struct platform_dri ...

  3. Android开发项目中常用到的开源库

    圆形头像 https://github.com/hdodenhof/CircleImageView ButterKnife https://github.com/JakeWharton/butterk ...

  4. Flask简单http接口实现

    # flask demo from flask import Flask, request app = Flask(__name__) # http://127.0.0.1:8080 @app.rou ...

  5. 搜索相关性算法在 DiDi Food 中的搜索

    导读:今天给大家分享的主题是搜索匹配问题在 DiDi Food 中的一些探索与应用.本文首先介绍了搜索相关性的一些背景,之后介绍了业界常见的三种匹配模型,以及在DiDi Food业务中的模型效果对比. ...

  6. Spring WebFlux 学习笔记 - (一) 前传:学习Java 8 Stream Api (2) - Stream的中间操作

    Stream API Java8中有两大最为重要的改变:第一个是 Lambda 表达式:另外一个则是 Stream API(java.util.stream.*). Stream 是 Java8 中处 ...

  7. Android学习笔记使用Notication 显示通知

    实现步骤 代码实现 创建MainActivity和DetailActivity(点击通知后要跳转的Activity),两个Activity的布局文件就是添加一张全屏的背景图,老规矩,不粘贴. Main ...

  8. ca13a_c++_顺序容器的操作6删除元素

    /*ca13a_c++_顺序容器的操作6删除元素c.erase(p) //删除迭代器p指向的位置c.erase(b,e) //删除b to e之间的数据,迭代器b包括,e不包括c.clear()//删 ...

  9. WeChair——团队展示

    这个作业属于哪个课程 软件工程 (福州大学至诚学院 - 计算机工程系) 团队名称 WeChair 这个作业要求在哪里 团队作业第一次 这个作业的目标 团队合作,项目开发 作业正文 如下 其他参考文献 ...

  10. AndroidStudio 编译失败;Caused by: org.gradle.api.resources.ResourceException: Could not get resource..

    错误信息: Caused by: org.gradle.api.resources.ResourceException: Could not get resource 'https://jcenter ...