Day102.尚医通: 项目搭建、Swagger2、统一返回结果、条件分页查询、增删改查、统一异常处理

目录

 

一、项目介绍

二、项目搭建

1、配置Swagger2  (Web版说明文档)  ★★

2、统一返回结果  (delete测试)  ★

3、实现带条件带分页查询接口  ★

带参数的分页查询  (wrapper 查询构造器) ★★

4、新增业务

5、修改业务

6、批量删除

7、锁定医院设置

8、统一异常处理  ★

自定义异常

9、统一日志处理 (了解)


一、项目介绍

1、业务流程 

2、系统架构

架构设计需要考虑的几个方面:成本、性能(分布式部署,引入缓存)、可扩展性(使用微服务架构,引入消息中间件)、高可用:集群、负载均衡、安全性。

Git:分布式版本控制工具

Docker:容器虚拟化技术

Nginx:反向代理、负载均衡、动静分离。

集群:防止单机故障、用户无感知。

Redis集群,三主三从最少六台

ELK日志系统 
Kibana:数据展示
Logstash:存储,记录、分析用户行为
ElasticSearch:搜索引擎

二、项目搭建

 2、项目搭建分层

*jar:java基础工程

*war:web工程包

*pom:父工程

依赖使用时才会下载,

1、配置Swagger2  (Web版说明文档)  ★★

前后端分离开发模式中,api文档是最好的沟通方式Swagger 根据代码生成Web版说明文档。

Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。

  1. 及时性 (接口变更后,能够及时准确地通知相关前后端开发人员)
  2. 规范性 (并且保证接口的规范性,如接口的地址,请求方式,参数及响应格式和错误信息)
  3. 一致性 (接口信息一致,不会出现因开发人员拿到的文档版本不一致,而出现分歧)
  4. 可测性 (直接在接口文档上进行测试,以方便理解业务)

常用注解

@EnableSwagger2    Swagger支持。声明在配置类“@Configuration”

@Api(description = "医院设置接口")   定义接口说明

@ApiOperation(value = "医院设置列表")   定义方法说明

@ApiModelProperty(value = "是否成功")   定义属性说明

@ApiParam(name = "id", value = "讲师ID", required = true)   定义参数说明

1. common下面创建子模块service_utils,创建配置类Swagger2Config @EnableSwagger2

package com.atguigu.yygh.common.config;


import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import springfox.documentation.service.Contact;

@Configuration
@EnableSwagger2
public class Swagger2Config {
    @Bean
    public Docket webApiConfig(){
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("webApi")
                .apiInfo(webApiInfo())
                .select()
                //只显示api路径下的页面
                //.paths(Predicates.and(PathSelectors.regex("/api/.*")))
                .build();
    }

    @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 webApiInfo(){
        return new ApiInfoBuilder()
                .title("网站-API文档")
                .description("本文档描述了网站微服务接口定义")
                .version("1.0")
                .contact(new Contact("atguigu", "http://atguigu.com", "493211102@qq.com"))
                .build();
    }

    private ApiInfo adminApiInfo(){
        return new ApiInfoBuilder()
                .title("后台管理系统-API文档")
                .description("本文档描述了后台管理系统微服务接口定义")
                .version("1.0")
                .contact(new Contact("atguigu", "http://atguigu.com", "49321112@qq.com"))
                .build();
    }
}

2. service 项目添加依赖

<dependency>
    <groupId>com.atguigu</groupId>
    <artifactId>service_utils</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

3. 启动类添加注解

@SpringBootApplication
@ComponentScan(basePackages = {"com.atguigu"}) //扫描其他项目配置
public class ServiceHospApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServiceHospApplication.class, args);
    }
}

4. 启动访问api,测试
http://localhost:8201/swagger-ui.html

5、优化 

@Api(description = "医院设置接口")   定义接口说明和参数说明

@ApiOperation(value = "医院设置列表")

2、统一返回结果  (delete测试)  ★

项目中我们会将响应封装成json返回,一般我们会将所有接口的数据格式统一, 使前端(iOS Android, Web)对数据的操作更一致、轻松。节省前端开发代码。

1、json
(1)对象:{“name”:”zhang3”,“age”:33}
(2)集合:
[{“name”:”zhang3”,“age”:33},
{“name”:”zhang3”,“age”:33},{“name”:”zhang3”,“age”:33}]

2、统一格式
{
  "success": true,
  "code": 20000,
  "message": "成功",
  "data": {
    "items": [
      {
        "id": "1",
        "name": "刘德华",
        "intro": "毕业于师范大学数学系,热爱教育事业,执教数学思维6年有余"
      }
    ]
  }
}

在common模块下创建子模块common_utils

1. 创建接口定义返回码

public interface ResultCode {
    public static Integer SUCCESS = 20000;

    public static Integer ERROR = 20001;
}

2.  封装结果类

package com.atguigu.yygh.common;

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.util.HashMap;
import java.util.Map;

/**
 * @Date 2022/7/13 11:27
 * @Author by:Plisetsky
 */
@Data
public class R {
    @ApiModelProperty(value = "是否成功")
    private Boolean success;

    @ApiModelProperty(value = "返回码")
    private Integer code;

    @ApiModelProperty(value = "返回消息")
    private String message;

    @ApiModelProperty(value = "返回数据")
    private Map<String, Object> data = new HashMap<String, Object>();

    private R(){}

    public static R ok(){
        R r = new R();
        r.setSuccess(true);
        r.setCode(ResultCode.SUCCESS);
        r.setMessage("成功");
        return r;
    }

    public static R error(){
        R r = new R();
        r.setSuccess(false);
        r.setCode(ResultCode.ERROR);
        r.setMessage("失败");
        return r;
    }

    public R success(Boolean success){
        this.setSuccess(success);
        return this;
    }

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

3. 引入依赖

<dependency>
    <groupId>com.atguigu</groupId>
    <artifactId>common_utils</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

4. controller 接口改造

    //查询所有医院设置
    @ApiOperation(value = "医院设置列表")
    @GetMapping("findAll")
    public R findAll() { //结果对象封装
        List<HospitalSet> list = hospitalSetService.list();
        return R.ok().data("list",list);
    }

    //删除   测试引入swagger2
    @ApiOperation(value = "医院设置删除")
    @DeleteMapping("{id}") //类型如果不匹配,影响速度
    public R removeById(@PathVariable Long id){
        boolean remove = hospitalSetService.removeById(id);
        return R.ok();
    }

3、实现带条件带分页查询接口  

1. 分析接口

确认需求:

需要的参数:当前页,每页记录数

返回值:R (Page 分页对象)

2. 添加分页插件


@Configuration
@EnableTransactionManagement
@MapperScan("com.atguigu.yygh.hosp.mapper")
public class HospConfig {

    //分页插件
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }
    
}

3. 实现接口

    @ApiOperation(value = "分页查询医院设置")
    @GetMapping("{page}/{limit}")
    public R pageList(@PathVariable Long page,
                      @PathVariable Long limit){

        //1.创建分页对象,传入参数 当前页 每页记录数
        Page<HospitalSet> pageParam = new Page<>(page,limit);

        //2.分页查询
        Page<HospitalSet> pageModel = hospitalSetService.page(pageParam);

        //3.封装结果
        return R.ok().data("pageModel",pageModel);
    }

4. 测试

 

带参数的分页查询  (wrapper 查询构造器) ★★

1. 分析接口

确认需求:

需要的参数:当前页、每页记录数、查询条件对象(vo:View Object)

返回值:R (Page 分页对象)

2. 实现接口

@RequestBody :Json 转 Java

@ResponseBod:Java 转 Json串

ApiOperation(value = "条件的分页查询医院设置")
    //@GetMapping("pageQuery/{page}/{limit}")
    @PostMapping("pageQuery/{page}/{limit}")
    public R pageList(@PathVariable Long page,
                      @PathVariable Long limit,
                      @RequestBody HospitalSetQueryVo hospitalSetQueryVo){
        //1.获取参数,验空,存入 wrapper 条件查询构造器
        QueryWrapper<HospitalSet> wrapper = new QueryWrapper<>();
        String hosname = hospitalSetQueryVo.getHosname();
        String hoscode = hospitalSetQueryVo.getHoscode();
        if(!StringUtils.isEmpty(hosname)){
            //模糊查询
            wrapper.like("hosname",hosname);
        }
        if(!StringUtils.isEmpty(hoscode)){
            wrapper.eq("hoscode",hoscode);
        }

        //2.创建分页对象,传入参数 当前页 每页记录数
        Page<HospitalSet> pageParam = new Page<>(page,limit);

        //3.分页查询,传入wrapper条件查询构造器
        Page<HospitalSet> pageModel = hospitalSetService.page(pageParam,wrapper);

        //4.封装结果返回
        return R.ok().data("pageModel",pageModel);
    }

3. 测试结果

 

4、新增业务

1. 分析接口

需要的参数:HospitalSet

返回值:R.ok()

2. 实现接口

    @ApiOperation(value = "新增医院设置")
    @PostMapping("save")
    public R save(@RequestBody HospitalSet hospitalSet){

        boolean save = hospitalSetService.save(hospitalSet);

        return save ? R.ok() : R.error();
    }

注意 测试时把时间和id删掉,会自动生成,hoscode外键唯一不能重复

Mysql5.5会出现的问题:

Mysql5.5需要使用MP自动填充日期类

 在实体中添加注解参数

5、修改业务

1. 分析接口

*数据回显:

参数:id

返回值:HospitalSet

*更新保存

      参数:HospitalSet

      返回值:R.ok()

2. 实现接口

    @ApiOperation(value = "根据id查询医院设置")
    @GetMapping("getHospsetById/{id}")
    public R getById(@PathVariable Long id){
        HospitalSet hospitalSet = hospitalSetService.getById(id);
        return R.ok().data("hospitalSet",hospitalSet);
    }

    @ApiOperation(value = "修改医院设置")
    @PostMapping("update")
    public R update(@RequestBody HospitalSet hospitalSet){
        //hospitalSet,存在id
        boolean save = hospitalSetService.updateById(hospitalSet);

        return save ? R.ok() : R.error();
    }

6、批量删除

1. 分析需求

参数:List<Long> 勾选的Id集合

返回值:R.ok()

2. 接口实现

    //删除   测试引入swagger2
    @ApiOperation(value = "批量删除医院设置")
    @DeleteMapping("batchRemove")
    public R removeById(@RequestBody List<Long> idList){
        boolean remove = hospitalSetService.removeByIds(idList);
        
        return remove ? R.ok() : R.error();
    }

3. 测试 

7、锁定医院设置

1. 分析需求

参数:id,status

返回值:R.ok()

2. 接口实现

    // 医院设置锁定和解锁
    @PutMapping("lockHospitalSet/{id}/{status}")
    public R lockHospitalSet(@PathVariable Long id,
                             @PathVariable Integer status) {
        //1.先查询
        HospitalSet hospitalSet = hospitalSetService.getById(id);
        //2.后更新
        hospitalSet.setStatus(status);
        boolean update = hospitalSetService.updateById(hospitalSet);
        return update ? R.ok() : R.error();
    }

8、统一异常处理  

出于系统信任度、安全考虑,不能将错误信息暴露

@ControllerAdvice  spring AOP面向切面编程,对Controller进行切面环绕。作用:全局异常处理、全局数据预处理、全局数据绑定

@ExceptionHandler (Exception.class)  异常拦截器(自定义异常处理器),需要结合@ControllerAdvice一起使用

service_utils 项目下

1. 添加common项目依赖

    <dependencies>
        <dependency>
            <groupId>com.atguigu</groupId>
            <artifactId>common_utils</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>

2. 创建统一异常处理器 AOP?

@ControllerAdvice //AOP面向切面编程
public class GlobalExceptionHandler {

    //抓取运行时异常
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public R error(Exception e){
        e.printStackTrace();
        return R.error();
    }
    //特殊异常处理
    @ExceptionHandler(ArithmeticException.class)
    @ResponseBody
    public R error(ArithmeticException e){
        e.printStackTrace();
        return R.error().message("特殊异常处理");
    }
}

自定义异常

service_utils 项目下

1. handler包内 创建自定义异常

//预约异常
@Data
@AllArgsConstructor
@NoArgsConstructor
public class YyghException extends RuntimeException{

    @ApiModelProperty(value = "状态码")
    private Integer code;
    @ApiModelProperty(value = "异常信息")
    private String msg;
}

2. 异常控制器 添加方法

    //自定义异常处理
    @ExceptionHandler(YyghException.class)
    @ResponseBody
    public R error(YyghException e){
        e.printStackTrace();
        return R.error().code(e.getCode()).message(e.getMessage());
    }

3. 方法手动抛出异常

    //查询所有医院设置
    @ApiOperation(value = "医院设置列表")
    @GetMapping("findAll")
    public R findAll() { //结果对象封装
        try {
            int i = 10/0;
        } catch (Exception e) {
            e.printStackTrace();
            throw new YyghException(20001,"自定义异常");
        }

        List<HospitalSet> list = hospitalSetService.list();
        return R.ok().data("list",list);
    }

9、统一日志处理 (了解)

配置日志级别

日志记录器(Logger)的行为是分等级的。如下表所示:

分为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL

默认情况下,spring boot从控制台打印出来的日志级别只有INFO及以上级别,可以配置日志级别

# 设置日志级别
logging.level.root=WARN

这种方式只能将日志打印在控制台上

sout 会产生io,需要删掉

二、Logback日志

spring boot内部使用Logback作为日志实现的框架。

Logback和log4j非常相似,如果你对log4j很熟悉,那对logback很快就会得心应手。

logback相对于log4j的一些优点:比log4j更好的logback、简介和优点详解_小雄哥的博客-CSDN博客_logback优点

1、配置logback日志

删除application.properties中的日志配置

安装idea彩色日志插件:grep-console

resources 中创建 logback-spring.xml 

<?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="D:/guli_log/edu" />

    <!-- 彩色日志 -->
    <!-- 配置格式变量: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.guli" 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>

将错误日志输出到文件  

GlobalExceptionHandler.java 中,类上添加注解   @Slf4j 

异常输出语句   log.error(e.getMessage());

将日志堆栈信息输出到文件

定义工具类

guli-framework-common下创建util包,创建ExceptionUtil.java工具类

package com.guli.common.util;

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

调用

log.error(ExceptionUtil.getMessage(e));

GuliException中创建toString方法

@Override
public String toString() {
    return "GuliException{" +
        "message=" + this.getMessage() +
        ", code=" + code +
        '}';
}

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值