4、后端接口工程搭建
4.1、创建尚融宝接口工程
4.1.1、需求
积分等级CRUD列表和表单


4.1.2、创建父工程srb
4.1.2.1、创建SpringBoot项目
Group:com.atguigu
Artifact:srb
4.1.2.2、删除src目录
4.1.2.3、配置SpringBoot版本
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
</parent>
4.1.2.4、配置pom依赖版本号
<properties>
<java.version>1.8</java.version>
<spring-cloud-alibaba.version>2.2.2.RELEASE</spring-cloud-alibaba.version>
<spring-cloud.version>Hoxton.SR8</spring-cloud.version>
<mybatis-plus.version>3.4.1</mybatis-plus.version>
<velocity.version>2.0</velocity.version>
<swagger.version>2.9.2</swagger.version>
<swagger-bootstrap-ui.version>1.9.2</swagger-bootstrap-ui.version>
<commons-lang3.version>3.9</commons-lang3.version>
<commons-fileupload.version>1.3.1</commons-fileupload.version>
<commons-io.version>2.6</commons-io.version>
<alibaba.easyexcel.version>2.1.1</alibaba.easyexcel.version>
<apache.xmlbeans.version>3.1.0</apache.xmlbeans.version>
<fastjson.version>1.2.28</fastjson.version>
<gson.version>2.8.2</gson.version>
<json.version>20170516</json.version>
<aliyun-java-sdk-core.version>4.3.3</aliyun-java-sdk-core.version>
<aliyun-sdk-oss.version>3.10.2</aliyun-sdk-oss.version>
<jodatime.version>2.10.1</jodatime.version>
<jwt.version>0.7.0</jwt.version>
<httpclient.version>4.5.1</httpclient.version>
</properties>
4.1.2.5、配置pom依赖
<dependencyManagement>
<dependencies>
<!--Spring Cloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--Spring Cloud Alibaba-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!--mybatis-plus 代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- Mybatis Plus 代码生成器模板引擎, -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>${velocity.version}</version>
</dependency>
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<!--swagger ui-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<!--swagger-bootstrap-ui-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>${swagger-bootstrap-ui.version}</version>
</dependency>
<!--commons-lang3-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<!--文件上传-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>${commons-fileupload.version}</version>
</dependency>
<!--commons-io-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<!--excel解析-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>${alibaba.easyexcel.version}</version>
</dependency>
<!--excel解析依赖-->
<dependency>
<groupId>org.apache.xmlbeans</groupId>
<artifactId>xmlbeans</artifactId>
<version>${apache.xmlbeans.version}</version>
</dependency>
<!--json-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>${json.version}</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${gson.version}</version>
</dependency>
<!--阿里云SDK远程调用-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>${aliyun-java-sdk-core.version}</version>
</dependency>
<!--阿里云文件管理-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>${aliyun-sdk-oss.version}</version>
</dependency>
<!--日期时间工具-->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>${jodatime.version}</version>
</dependency>
<!--jwt工具-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jwt.version}</version>
</dependency>
<!--httpclient-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
4.1.3、创建模块guigu-common
4.1.3.1、创建Maven模块
在srb下创建普通maven模块
Group:com.atguigu
Artifact:guigu-common
4.1.3.2、配置pom
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--lombok用来简化实体类:需要安装lombok插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
4.1.4、创建模块service-base
4.1.4.1、创建Maven模块
在srb下创建普通maven模块
Group:com.atguigu
Artifact:service-base
4.1.4.2、配置pom
注意:依赖guigu-common
<dependencies>
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>guigu-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
</dependency>
<!--swagger ui-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>
</dependencies>
4.1.5、创建模块service-core
4.1.5.1、创建Maven模块
在srb下创建普通maven模块
Group:com.atguigu
Artifact:service-core
4.1.5.2、配置pom
注意:依赖service-base
<dependencies>
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>service-base</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!--mybatis-plus 代码生成器-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
</dependency>
<!-- Mybatis Plus 代码生成器模板引擎, -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
</dependency>
</dependencies>
4.1.6、代码生成器
4.1.6.1、创建数据库
创建数据库srb_core
并执行sql脚本初始化数据结构和数据
4.1.6.2、创建代码生成器
在test目录中创建测试用例,并执行
package com.atguigu.srb.core;
public class CodeGenerator {
@Test
public void genCode() {
// 1、创建代码生成器
AutoGenerator mpg = new AutoGenerator();
// 2、全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("Helen");
gc.setOpen(false); //生成后是否打开资源管理器
gc.setServiceName("%sService"); //去掉Service接口的首字母I
gc.setIdType(IdType.AUTO); //主键策略
gc.setSwagger2(true);//开启Swagger2模式
mpg.setGlobalConfig(gc);
// 3、数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/srb_core?serverTimezone=GMT%2B8&characterEncoding=utf-8");
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.setParent("com.atguigu.srb.core");
pc.setEntity("pojo.entity"); //此对象与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。
mpg.setPackageInfo(pc);
// 5、策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
strategy.setEntityLombokModel(true); // lombok
strategy.setLogicDeleteFieldName("is_deleted");//逻辑删除字段名
strategy.setEntityBooleanColumnRemoveIsPrefix(true);//去掉布尔值的is_前缀(确保tinyint(1))
strategy.setRestControllerStyle(true); //restful api风格控制器
mpg.setStrategy(strategy);
// 6、执行
mpg.execute();
}
}
4.1.7、启动应用程序
4.1.7.1、创建application.yml
server:
port: 8110 # 服务端口
spring:
profiles:
active: dev # 环境设置
application:
name: service-core # 服务名
datasource: # mysql数据库连接
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/srb_core?serverTimezone=GMT%2B8&characterEncoding=utf-8
username: root
password: 123456
mybatis-plus: #mybatis
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath:com/atguigu/srb/core/mapper/xml/*.xml
4.1.7.2、创建SpringBoot配置文件
在service-core中创建config包,创建MybatisPlusConfig类
package com.atguigu.srb.core.config;
@Configuration
@MapperScan("com.atguigu.srb.core.mapper")
@EnableTransactionManagement //事务处理
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//分页
return interceptor;
}
}
4.1.7.3、创建SpringBoot启动类
注意:扫描com.atguigu.srb
package com.atguigu.srb.core;
@SpringBootApplication
@ComponentScan({"com.atguigu.srb"})
public class ServiceCoreApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceCoreApplication.class, args);
}
}
4.1.7.4、运行启动类
查看控制台8110端口是否成功启动
4.2、积分等级CRUD
4.2.1、积分等级列表接口
4.2.1.1、编写积分等级管理接口
在controller中添加admin包,添加AdminIntegralGradeController类
package com.atguigu.srb.core.controller.admin;
@CrossOrigin
@RestController
@RequestMapping("/admin/core/integralGrade")
public class AdminIntegralGradeController {
@Resource
private IntegralGradeService integralGradeService;
@GetMapping("/list")
public List<IntegralGrade> listAll(){
return integralGradeService.list();
}
}
4.2.1.2、测试
重启服务,访问: http://localhost:8110/admin/core/integralGrade/list 查看结果json数据
4.2.2、逻辑删除接口
4.2.2.1、添加删除方法
AdminIntegralGradeController添加removeById方法
@DeleteMapping("/remove/{id}")
public boolean removeById(@PathVariable Long id){
return integralGradeService.removeById(id);
}
4.2.2.2、使用postman测试删除

4.2.3、配置Swagger2
4.2.3.1、Swagger2配置文件
在service-base中创建Swagger2Config
package com.atguigu.srb.base.config;
@Configuration
@EnableSwagger2
public class Swagger2Config {
@Bean
public Docket adminApiConfig(){
return new Docket(DocumentationType.SWAGGER_2)
.groupName("adminApi")
.apiInfo(adminApiInfo())
.select()
//只显示admin路径下的页面
.paths(Predicates.and(PathSelectors.regex("/admin/.*")))
.build();
}
private ApiInfo adminApiInfo(){
return new ApiInfoBuilder()
.title("尚融宝后台管理系统-API文档")
.description("本文档描述了尚融宝后台管理系统接口")
.version("1.0")
.contact(new Contact("Helen", "http://atguigu.com", "55317332@qq.com"))
.build();
}
}
4.2.3.2、查看Swagger文档
重启服务器查看接口文档:http://localhost:8110/swagger-ui.html
4.2.3.3、常见注解
**实体类注解:**entity的实体类中可以添加一些自定义设置,例如:
@ApiModelProperty(value = "创建时间", example = "2019-01-01 8:00:00")
private LocalDateTime createTime;
@ApiModelProperty(value = "更新时间", example = "2019-01-01 8:00:00")
private LocalDateTime updateTime;
controller注解:
定义在类上
@Api(tags = "积分等级管理")
定义在方法上
@ApiOperation("积分等级列表")
@ApiOperation(value = "根据id删除积分等级", notes = "逻辑删除")
定义在参数上
@ApiParam(value = "数据id", required = true, example = "100")
4.3、统一返回结果
4.3.1、定义统一返回结果
4.3.1.1、数据格式的定义
项目中我们会将响应封装成json返回,一般我们会将所有接口的数据格式统一, 使前端对数据的操作更一致、轻松。
一般情况下,统一返回数据格式没有固定的格式,只要能描述清楚返回的数据状态以及要返回的具体数据就可以。但是一般会包含状态码、返回消息、数据这几部分内容
例如,我们的系统要求返回的基本数据格式如下:
成功:
{
"code": 0,
"message": "成功",
"data": 数据
}
失败:
{
"code": -1,
"message": "失败",
"data": null
}
因此,我们定义统一结果
{
"code": 数字, //业务响应码
"message": 字符串, //返回消息
"data": 对象 //返回数据
}
4.3.1.2、创建枚举
在guigu-common中创建result包,创建枚举 ResponseEnum
package com.atguigu.common.result;
@Getter
@AllArgsConstructor
@ToString
public enum ResponseEnum {
SUCCESS(0, "成功"),
ERROR(-1, "服务器内部错误"),
;
// 响应状态码
private Integer code;
// 响应信息
private String message;
}
完整的枚举源代码:ResponseEnum.java
4.3.1.3、定义同统一结果类
package com.atguigu.common.result;
@Data
public class R {
private Integer code;
private String message;
private Map<String, Object> data = new HashMap();
/**
* 构造器私有
*/
private R(){}
/**
* 返回成功
*/
public static R ok(){
R r = new R();
r.setCode(ResponseEnum.SUCCESS.getCode());
r.setMessage(ResponseEnum.SUCCESS.getMessage());
return r;
}
/**
* 返回失败
*/
public static R error(){
R r = new R();
r.setCode(ResponseEnum.ERROR.getCode());
r.setMessage(ResponseEnum.ERROR.getMessage());
return r;
}
/**
* 设置特定结果
*/
public static R setResult(ResponseEnum responseEnum){
R r = new R();
r.setCode(responseEnum.getCode());
r.setMessage(responseEnum.getMessage());
return r;
}
public R message(String message){
this.setMessage(message);
return this;
}
public R code(Integer code){
this.setCode(code);
return this;
}
public R data(String key, Object value){
this.data.put(key, value);
return this;
}
public R data(Map<String, Object> map){
this.setData(map);
return this;
}
}
4.3.2、使用统一返回结果
4.3.2.1、修改listAll
@ApiOperation("积分等级列表")
@GetMapping("/list")
public R listAll(){
List<IntegralGrade> list = integralGradeService.list();
return R.ok().data("list", list);
}
4.3.2.2、修改removeById
@ApiOperation(value = "根据id删除积分等级", notes="逻辑删除")
@DeleteMapping("/remove/{id}")
public R removeById(
@ApiParam(value = "数据id", required = true, example = "1")
@PathVariable Long id){
boolean result = integralGradeService.removeById(id);
if(result){
//return R.setResult(ResponseEnum.UPLOAD_ERROR);
return R.ok().message("删除成功");
}else{
return R.error().message("删除失败");
}
}
4.3.2.3、新增数据
@ApiOperation("新增积分等级")
@PostMapping("/save")
public R save(
@ApiParam(value = "积分等级对象", required = true)
@RequestBody IntegralGrade integralGrade){
boolean result = integralGradeService.save(integralGrade);
if (result) {
return R.ok().message("保存成功");
} else {
return R.error().message("保存失败");
}
}
4.3.2.4、根据id查询
@ApiOperation("根据id获取积分等级")
@GetMapping("/get/{id}")
public R getById(
@ApiParam(value = "数据id", required = true, example = "1")
@PathVariable Long id
){
IntegralGrade integralGrade = integralGradeService.getById(id);
if(integralGrade != null){
return R.ok().data("record", integralGrade);
}else{
return R.error().message("数据不存在");
}
}
4.3.2.5、根据id修改
@ApiOperation("更新积分等级")
@PutMapping("/update")
public R updateById(
@ApiParam(value = "积分等级对象", required = true)
@RequestBody IntegralGrade integralGrade){
boolean result = integralGradeService.updateById(integralGrade);
if(result){
return R.ok().message("修改成功");
}else{
return R.error().message("修改失败");
}
}
4.4、统一异常处理
4.4.1、项目中的异常
4.4.1.1、制造异常
屏蔽 IntegralGrade 中的 @TableField注解
@ApiModelProperty(value = "逻辑删除(1:已删除,0:未删除)")
//@TableField("is_deleted")
@TableLogic
private Boolean deleted;
4.4.1.2、Swagger中测试
测试列表查询功能,查看结果,发生错误,显示响应失败

4.4.2、统一异常处理
目标:我们想让异常结果也显示为统一的返回结果对象,并且统一处理系统的异常信息,那么需要进行统一异常处理。
4.4.2.1、创建统一异常处理器
guigu-common中创建exception包,创建统一异常处理器类UnifiedExceptionHandler
package com.atguigu.common.exception;
@Slf4j
@Component //Spring容易自动管理
@RestControllerAdvice //在controller层添加通知。如果使用@ControllerAdvice,则方法上需要添加@ResponseBody
public class UnifiedExceptionHandler {
/**
* 未定义异常
*/
@ExceptionHandler(value = Exception.class) //当controller中抛出Exception,则捕获
public R handleException(Exception e) {
log.error(e.getMessage(), e);
return R.error();
}
}
4.4.2.2、service-core添加扫描
添加 “com.atguigu.common”
@SpringBootApplication
@ComponentScan({"com.atguigu.srb", "com.atguigu.common"})
public class ServiceCoreApplication {
4.4.2.3、测试
返回统一错误结果

4.4.3、处理特定异常
如果我们不想显示统一的“服务器内部错误”,需要个性化的显示异常信息,那么需要针对特定的异常做处理
4.4.3.1、添加依赖
在guigu-common中添加jdbc依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
4.4.3.2、添加异常处理方法
在 UnifiedExceptionHandler 中添加
/**
* 特定异常
*/
@ExceptionHandler(BadSqlGrammarException.class)
public R handleBadSqlGrammarException(BadSqlGrammarException e){
log.error(e.getMessage(), e);
return R.setResult(ResponseEnum.BAD_SQL_GRAMMAR_ERROR);
}
4.4.3.3、测试

问题:上面的例子虽然针对特定的异常显示个性化的错误信息,但是你会发现,针对每个不同的异常我们都需要在项目中添加对应的处理方法,并捕获对应的异常对象,可能还要针对这个异常添加额外的依赖。这显然不是最好的方式。
方案:此类异常直接抛出,并且用Exception类捕获就可以了。
4.4.3.4、恢复制造的异常
@TableField(value = "is_deleted")
4.4.4、自定义异常
目标:使用一个或较少的异常类,可以捕获和显示所有的异常信息。
方案:因此,我们可以创建一个自定义异常类(必须是运行时异常),在程序中抛出这个自定义异常对象,并在统一异常处理器中捕获自定义异常对象
4.4.4.1、创建自定义异常类
package com.atguigu.common.exception;
@Data
@NoArgsConstructor
public class BusinessException extends RuntimeException {
//状态码
private Integer code;
//错误消息
private String message;
}
完整的源代码:BusinessException.java
4.4.4.2、添加异常处理方法
UnifiedExceptionHandler类中添加
/**
* 自定义异常
*/
@ExceptionHandler(BusinessException.class)
public R handleBusinessException(BusinessException e){
log.error(e.getMessage(), e);
return R.error().message(e.getMessage()).code(e.getCode());
}
4.4.4.3、修改Controller
在AdminIntegralGradeController的方法中添加异常处理,业务中需要的位置抛出BusinessException自定义异常。
@ApiOperation("新增积分等级")
@PostMapping("/save")
public R save(
@ApiParam(value = "积分等级对象", required = true)
@RequestBody IntegralGrade integralGrade){
//如果借款额度为空就手动抛出一个自定义的异常!
if(integralGrade.getBorrowAmount() == null){
//BORROW_AMOUNT_NULL_ERROR(-201, "借款额度不能为空"),
throw new BusinessException(ResponseEnum.BORROW_AMOUNT_NULL_ERROR);
}
boolean result = integrationService.save(integralGrade);
if (result) {
return R.ok().message("保存成功");
} else {
return R.error().message("保存失败");
}
}
4.4.4.4、测试
测试

结果

4.4.5、异常处理优化
目标:以优雅的 Assert(断言) 方式来校验业务的异常情况,消除 if else
4.4.5.1、什么是断言
package com.atguigu.srb.core;
import org.junit.jupiter.api.Test;
import org.springframework.util.Assert;
public class AssertTests {
//if else的用法
@Test
public void test1() {
Object o = null;
if (o == null) {
throw new IllegalArgumentException("用户不存在.");
}
}
//断言的用法:更为简洁
@Test
public void test2() {
// 另一种写法
Object o = null;
Assert.notNull(o, "用户不存在.");
}
}
4.4.5.2、自定义断言
用断言的方式封装异常的抛出
package com.atguigu.common.exception;
@Slf4j
public abstract class Assert {
/**
* 断言对象不为空
* 如果对象obj为空,则抛出异常
* @param obj 待判断对象
*/
public static void notNull(Object obj, ResponseEnum responseEnum) {
if (obj == null) {
log.info("obj is null...............");
throw new BusinessException(responseEnum);
}
}
}
完整的源代码:Assert.java
4.4.5.3、修改controller
在controller中用断言替换if else
Assert.notNull(integralGrade.getBorrowAmount(), ResponseEnum.BORROW_AMOUNT_NULL_ERROR);
4.4.6、Controller上层异常
4.4.6.1、异常分类
对异常按阶段进行分类,大体可以分成:进入Controller前的异常 和 业务层异常,具体可以参考下图:

4.4.6.2、处理Controller上层异常
UnifiedExceptionHandler中添加
/**
* Controller上一层相关异常
*/
@ExceptionHandler({
NoHandlerFoundException.class,
HttpRequestMethodNotSupportedException.class,
HttpMediaTypeNotSupportedException.class,
MissingPathVariableException.class,
MissingServletRequestParameterException.class,
TypeMismatchException.class,
HttpMessageNotReadableException.class,
HttpMessageNotWritableException.class,
MethodArgumentNotValidException.class,
HttpMediaTypeNotAcceptableException.class,
ServletRequestBindingException.class,
ConversionNotSupportedException.class,
MissingServletRequestPartException.class,
AsyncRequestTimeoutException.class
})
public R handleServletException(Exception e) {
log.error(e.getMessage(), e);
//SERVLET_ERROR(-102, "servlet请求异常"),
return R.error().message(ResponseEnum.SERVLET_ERROR.getMessage()).code(ResponseEnum.SERVLET_ERROR.getCode());
}
4.4.6.3、测试
在save测试用例中输入非法的json参数,则得到下面的结果。我们可以在控制台日志中查看具体的错误原因。前端只需要返回相对简单友好的提示即可。

4.5、统一日志处理
4.5.1、Logback日志
4.5.1.1、什么是日志
通过日志查看程序的运行过程,运行信息,异常信息等
4.5.1.2、日志级别
日志记录器(Logger)的行为是分等级的。如下表所示:
分为:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF
默认情况下,spring boot从控制台打印出来的日志级别只有INFO及以上级别,可以配置日志级别
# 设置日志级别
logging:
level:
root: ERROR
这种方式能将ERROR级别以及以上级别的日志输出到控制台上,其他级别将不会输出
4.5.1.3、创建日志文件
spring boot内部使用Logback作为日志实现的框架。
先删除前面在application.yml中的日志级别配置
resources 中创建 logback-spring.xml (默认日志文件的名字)
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
</configuration>
4.5.1.4、创建测试日志输出
将以下日志输出到任意controller的方法中即可,例如list方法中
@ApiOperation("积分等级列表")
@GetMapping("/list")
public R listAll(){
log.info("hi i'm helen");
log.warn("warning!!!");
log.error("it's a error");
List<IntegralGrade> list = integrationService.list();
return R.ok().data("list", list);
}
4.5.2、基本配置说明
4.5.2.1、configuration
日志配置的根节点
<configuration></configuration>
4.5.2.2、contextName
是的子节点。
每个logger都关联到logger上下文,默认上下文名称为“default”。但可以使用设置成其他名字,用于区分不同的应用程序。
<contextName>atguiguSrb</contextName>
4.5.2.3、property
是的子节点,用来定义变量。
有两个属性,name和value:name的值是变量的名称,value是变量的值。
通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。
<!-- 日志的输出目录 -->
<property name="log.path" value="D:/project/finance/srb_log/core" />
<!--控制台日志格式:彩色日志-->
<!-- magenta:洋红 -->
<!-- boldMagenta:粗红-->
<!-- cyan:青色 -->
<!-- white:白色 -->
<!-- magenta:洋红 -->
<property name="CONSOLE_LOG_PATTERN"
value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) %highlight([%-5level]) %green(%logger) %msg%n"/>
<!--文件日志格式-->
<property name="FILE_LOG_PATTERN"
value="%date{yyyy-MM-dd HH:mm:ss} [%-5level] %thread %file:%line %logger %msg%n" />
<!--编码-->
<property name="ENCODING"
value="UTF-8" />
4.5.2.4、appender
是的子节点,是负责写日志的组件
有两个必要属性name和class:name指定appender名称,class指定appender的全限定名
对日志进行格式化
定义日志的具体输出格式
编码方式
控制台日志配置
<!-- 控制台日志 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>${ENCODING}</charset>
</encoder>
</appender>
**文件日志配置 **
表示日志文件的位置,如果上级目录不存在会自动创建,没有默认值。
默认 true,日志被追加到文件结尾,如果是 false,服务重启后清空现存文件。
<!-- 文件日志 -->
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${log.path}/log.log</file>
<append>true</append>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>${ENCODING}</charset>
</encoder>
</appender>
4.5.2.5、logger
可以是的子节点,用来设置某一个包或具体某一个类的日志打印级别、指定
name:用来指定受此logger约束的某一个包或者具体的某一个类
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF。默认继承上级的级别
可以包含零个或多个元素,标识这个appender将会添加到这个logger
<!-- 日志记录器 -->
<logger name="com.atguigu" level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</logger>
4.5.2.6、测试
测试日志记录的控制台输出、文件输出、以及日志级别
4.5.3、多环境配置
springProfile
在一个基于Spring boot开发的项目里,常常需要有多套环境的配置:开发,测试以及产品。使用springProfile 可以分别配置开发(dev),测试(test)以及生产(prod)等不同的环境
<!-- 开发环境和测试环境 -->
<springProfile name="dev,test">
<logger name="com.atguigu" level="INFO">
<appender-ref ref="CONSOLE" />
</logger>
</springProfile>
<!-- 生产环境 -->
<springProfile name="prod">
<logger name="com.atguigu" level="ERROR">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</logger>
</springProfile>
4.5.4、滚动日志
问题:生产环境下,如果系统长时间运行,那么日志文件会变得越来越大,系统读取和写入日志的时间会越来越慢,严重的情况会耗尽系统内存,导致系统宕机。
解决方案:可以设置滚动日志。
4.5.4.1、设置时间滚动策略
RollingFileAppender是Appender的另一个实现,表示滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将旧日志备份到其他文件
是的子节点,用来定义滚动策略。
TimeBasedRollingPolicy:最常用的滚动策略,根据时间来制定滚动策略。
:包含文件名及转换符, “%d”可以包含指定的时间格式,如:%d{yyyy-MM-dd}。如果直接使用 %d,默认格式是 yyyy-MM-dd。:可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每个月滚动,且是6,则只保存最近6个月的文件,删除之前的旧文件。注意,删除旧文件是,那些为了归档而创建的目录也会被删除。
<appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 要区别于其他的appender中的文件名字 -->
<file>${log.path}/log-rolling.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>${ENCODING}</charset>
</encoder>
<!-- 设置滚动日志记录的滚动策略 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志归档路径以及格式 -->
<fileNamePattern>${log.path}/info/log-rolling-%d{yyyy-MM-dd}.log</fileNamePattern>
<!--归档日志文件保留的最大数量-->
<maxHistory>15</maxHistory>
</rollingPolicy>
</appender>
4.5.4.2、设置触发滚动时机
放在的子节点的位置,基于实践策略的触发滚动策略
设置触发滚动条件:单个文件大于100M时生成新的文件
注意:修改日志文件名 此时 ${log.path}/info/log-rolling-%d{yyyy-MM-dd}.%i.log
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>1KB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
4.5.5、完整的日志配置文件
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<contextName>atguiguSrb</contextName>
<!-- 日志的输出目录 -->
<property name="log.path" value="D:/project/test/srb_log/core" />
<!--控制台日志格式:彩色日志-->
<!-- magenta:洋红 -->
<!-- boldMagenta:粗红-->
<!-- cyan:青色 -->
<!-- white:白色 -->
<!-- magenta:洋红 -->
<property name="CONSOLE_LOG_PATTERN"
value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) %highlight([%-5level]) %green(%logger) %msg%n"/>
<!--文件日志格式-->
<property name="FILE_LOG_PATTERN"
value="%date{yyyy-MM-dd HH:mm:ss} [%-5level] %thread %file:%line %logger %msg%n" />
<!--编码-->
<property name="ENCODING"
value="UTF-8" />
<!-- 控制台日志 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>${ENCODING}</charset>
</encoder>
</appender>
<!-- 文件日志 -->
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${log.path}/log.log</file>
<append>true</append>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>${ENCODING}</charset>
</encoder>
</appender>
<appender name="ROLLING_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 要区别于其他的appender中的文件名字 -->
<file>${log.path}/log-rolling.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>${ENCODING}</charset>
</encoder>
<!-- 设置滚动日志记录的滚动策略 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志归档路径以及格式 -->
<fileNamePattern>${log.path}/info/log-rolling-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!--归档日志文件保留的最大数量-->
<maxHistory>15</maxHistory>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>1KB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
</appender>
<!-- <logger name="com.atguigu" level="INFO">-->
<!-- <appender-ref ref="CONSOLE" />-->
<!-- <appender-ref ref="FILE" />-->
<!-- </logger>-->
<!-- 开发环境和测试环境 -->
<springProfile name="dev,test">
<logger name="com.atguigu" level="INFO">
<appender-ref ref="CONSOLE" />
</logger>
</springProfile>
<!-- 生产环境 -->
<springProfile name="prod">
<logger name="com.atguigu" level="ERROR">
<appender-ref ref="CONSOLE" />
<appender-ref ref="ROLLING_FILE" />
</logger>
</springProfile>
</configuration>
本文章参考B站 尚硅谷《尚融宝》Java微服务分布式金融项目,仅供个人学习使用,部分内容为本人自己见解,与尚硅谷无关。
1620

被折叠的 条评论
为什么被折叠?



