谷粒学院-第二天-Swagger

目录

一、Swagger2介绍

二、配置Swagger2

三、统一返回数据格式

四、创建统一结果返回类 

五、统一返回结果使用

六、分页

七、条件查询

八、自动填充封装

九、什么是统一异常处理

 十、统一异常处理

 十一、处理特定异常

十二、日志

十三、Logback日志


一、Swagger2介绍

前后端分离开发模式中,api文档是最好的沟通方式。

Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。         1. 及时性 (接口变更后,能够及时准确地通知相关前后端开发人员)

        2. 规范性 (并且保证接口的规范性,如接口的地址,请求方式,参数及响应格式和错误信息)

        3. 一致性 (接口信息一致,不会出现因开发人员拿到的文档版本不一致,而出现分歧)

        4. 可测性 (直接在接口文档上进行测试,以方便理解业务)

二、配置Swagger2

1、创建common模块

在guli-parent下创建模块common

配置:

groupId:com.atguigu

artifactId:common

2、在common中引入相关依赖

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

        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <scope>provided </scope>
        </dependency>

        <!--lombok用来简化实体类:需要安装lombok插件-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided </scope>
        </dependency>

        <!--swagger-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <scope>provided </scope>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <scope>provided </scope>
        </dependency>

        <!-- redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <!-- spring2.X集成redis所需common-pool2
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.6.0</version>
        </dependency>-->
</dependencies>

3、在common下面创建子模块service-base

创建包com.atguigu.servicebase.config,创建类SwaggerConfig

@Configuration //配置类
@EnableSwagger2 //swagger注解
public class SwaggerConfig {

    @Bean
    public Docket webApiConfig(){
        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("webApi")
                .apiInfo(webApiInfo())
                .select()
                .paths(Predicates.not(PathSelectors.regex("/admin/.*")))
                .paths(Predicates.not(PathSelectors.regex("/error.*")))
                .build();

    }

    private ApiInfo webApiInfo(){

        return new ApiInfoBuilder()
                .title("网站-课程中心API文档")
                .description("本文档描述了课程中心微服务接口定义")
                .version("1.0")
                .contact(new Contact("java", "http://atguigu.com", "1123@qq.com"))
                .build();
    }
}

4、在模块service模块中引入service-base 

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

5、在service-edu启动类上添加注解,进行测试

6、API模型

可以添加一些自定义设置,例如: 定义样例数据

    @ApiModelProperty(value = "创建时间", example = "2019-01-01 8:00:00")
    @TableField(fill = FieldFill.INSERT)
    private Date gmtCreate;

    @ApiModelProperty(value = "更新时间", example = "2019-01-01 8:00:00")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date gmtModified;

 5、定义接口说明和参数说明

定义在类上:@Api

定义在方法上:@ApiOperation

定义在参数上:@ApiParam

@Api(description="讲师管理")
@RestController
@RequestMapping("/admin/edu/teacher")
public class TeacherAdminController {

     @Autowired
     private TeacherService teacherService;

     @ApiOperation(value = "所有讲师列表")
     @GetMapping
     public List<Teacher> list(){
         return teacherService.list(null);
     }

     @ApiOperation(value = "根据ID删除讲师")
     @DeleteMapping("{id}")
     public boolean removeById(
             @ApiParam(name = "id", value = "讲师ID", required = true)
             @PathVariable String id){
             return teacherService.removeById(id);
     }
}

三、统一返回数据格式

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

一般情况下,统一返回数据格式没有固定的格式,只要能描述清楚返回的数据状态以及要返回的具体数 据就可以。但是一般会包含状态码、返回消息、数据这几部分内容

例如,我们的系统要求返回的基本数据格式如下:

列表:

 分页:

没有返回数据:

 失败:

因此,我们定义统一结果 

四、创建统一结果返回类 

1、在common模块下创建子模块common-utils

2、创建接口定义返回码

创建包com.atguigu.commonutils,创建接口 ResultCode.java

public interface ResultCode {
    public static Integer SUCCESS = 2000;//成功

    public static Integer ERROR = 2001;//操作失败
}

3、创建结果类

创建类 R.java

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

五、统一返回结果使用

1、在service模块中添加依赖

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

2、修改Controller中的返回结果

列表

@ApiOperation(value = "所有讲师列表")
@GetMapping
public R list(){
     List<Teacher> list = teacherService.list(null);
     return R.ok().data("items", list);
}

删除

@ApiOperation(value = "根据ID删除讲师")
@DeleteMapping("{id}")
public R removeById(
     @ApiParam(name = "id", value = "讲师ID", required = true)
     @PathVariable String id){
     teacherService.removeById(id);
     return R.ok();
}

六、分页

1、MyBatisPlusConfig中配置分页插件

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

2、分页Controller方法

TeacherAdminController中添加分页方法

@ApiOperation(value = "分页讲师列表")
@GetMapping("{page}/{limit}")
public R pageList(
    @ApiParam(name = "page", value = "当前页码", required = true)
    @PathVariable Long page,
    @ApiParam(name = "limit", value = "每页记录数", required = true)
    @PathVariable Long limit){
    Page<Teacher> pageParam = new Page<>(page, limit);
    teacherService.page(pageParam, null);
    List<Teacher> records = pageParam.getRecords();
    long total = pageParam.getTotal();
    return R.ok().data("total", total).data("rows", records);
}

3、Swagger中测试

七、条件查询

根据讲师名称name,讲师头衔level、讲师入驻时间gmt_create(时间段)查询

1、创建查询对象

创建com.guli.edu.query包,创建TeacherQuery.java查询对象

package com.atguigu.eduservice.entity.vo;

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

@Data
public class TeacherQuery {
    @ApiModelProperty(value = "教师名称,模糊查询")
    private String name;

    @ApiModelProperty(value = "头衔 1高级讲师 2首席讲师")
    private Integer level;

    @ApiModelProperty(value = "查询开始时间", example = "2019-01-01 10:10:10")
    private String begin;//注意,这里使用的是String类型,前端传过来的数据无需进行类型转换

    @ApiModelProperty(value = "查询结束时间", example = "2019-12-01 10:10:10")
    private String end;
}

2、service

接口

package com.guli.edu.service;
public interface TeacherService extends IService<Teacher> {
     void pageQuery(Page<Teacher> pageParam, TeacherQuery teacherQuery);
}

实现

package com.guli.edu.service.impl;
@Service
public class TeacherServiceImpl extends ServiceImpl<TeacherMapper, Teacher>
implements TeacherService {
     @Override
     public void pageQuery(Page<Teacher> pageParam, TeacherQuery teacherQuery){

         QueryWrapper<Teacher> queryWrapper = new QueryWrapper<>();
         queryWrapper.orderByAsc("sort");
         if (teacherQuery == null){
             baseMapper.selectPage(pageParam, queryWrapper);
             return;
         }
         String name = teacherQuery.getName();
         Integer level = teacherQuery.getLevel();
         String begin = teacherQuery.getBegin();
         String end = teacherQuery.getEnd();
         if (!StringUtils.isEmpty(name)) {
             queryWrapper.like("name", name);
         }
         if (!StringUtils.isEmpty(level) ) {
             queryWrapper.eq("level", level);
         }
         if (!StringUtils.isEmpty(begin)) {
             queryWrapper.ge("gmt_create", begin);
         }
         if (!StringUtils.isEmpty(end)) {
             queryWrapper.le("gmt_create", end);
         }
         baseMapper.selectPage(pageParam, queryWrapper);
 }
}

3、controller

TeacherAdminController中修改 pageList方法:

增加参数TeacherQuery teacherQuery,非必选

@ApiOperation(value = "分页讲师列表")
@GetMapping("{page}/{limit}")
public R pageQuery(
    @ApiParam(name = "page", value = "当前页码", required = true)
    @PathVariable Long page,
    @ApiParam(name = "limit", value = "每页记录数", required = true)
    @PathVariable Long limit,
    @ApiParam(name = "teacherQuery", value = "查询对象", required = false)
    TeacherQuery teacherQuery){
    Page<Teacher> pageParam = new Page<>(page, limit);
    teacherService.pageQuery(pageParam, teacherQuery);
    List<Teacher> records = pageParam.getRecords();
    long total = pageParam.getTotal();
    return R.ok().data("total", total).data("rows", records);
}

4、Swagger中测试

八、自动填充封装

1、在service-base模块中添加

创建包handler,创建自动填充类 MyMetaObjectHandler

package com.atguigu.servicebase.handler;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import net.bytebuddy.asm.Advice;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.util.Date;

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

2、在实体类添加自动填充注解

    @ApiModelProperty(value = "创建时间")
    @TableField(fill = FieldFill.INSERT)
    private Date gmtCreate;

    @ApiModelProperty(value = "更新时间")
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date gmtModified;

二、controller方法定义

1、新增

 @ApiOperation(value = "新增讲师")
 @PostMapping
 public R save(
     @ApiParam(name = "teacher", value = "讲师对象", required = true)
     @RequestBody Teacher teacher){
     teacherService.save(teacher);
     return R.ok();
 }

2、根据id查询

 @ApiOperation(value = "根据ID查询讲师")
 @GetMapping("{id}")
 public R getById(
     @ApiParam(name = "id", value = "讲师ID", required = true)
     @PathVariable String id){
     Teacher teacher = teacherService.getById(id);
     return R.ok().data("item", teacher);
 }

3、根据id修改

 @ApiOperation(value = "根据ID修改讲师")
 @PutMapping("{id}")
 public R updateById(
     @ApiParam(name = "id", value = "讲师ID", required = true)
     @PathVariable String id,
     @ApiParam(name = "teacher", value = "讲师对象", required = true)
     @RequestBody Teacher teacher){
     teacher.setId(id);
     teacherService.updateById(teacher);
     return R.ok();
}

九、什么是统一异常处理

1、制造异常

除以0

int a = 10/0;

2、什么是统一异常处理

我们想让异常结果也显示为统一的返回结果对象,并且统一处理系统的异常信息,那么需要统一异常处 理

 十、统一异常处理

1、创建统一异常处理器

在service-base中创建统一异常处理类GlobalExceptionHandler.java:

@ControllerAdvice
public class GlobalExceptionHandler {

    //指定出现什么异常执行这个方法
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public R error(Exception e){
        e.printStackTrace();
        return R.error()
    }
}

2、测试

返回统一错误结果

 十一、处理特定异常

1、添加异常处理方法

GlobalExceptionHandler.java中添加

@ExceptionHandler(ArithmeticException.class)
@ResponseBody
public R error(ArithmeticException e){
     e.printStackTrace();
     return R.error().message("执行了自定义异常");
}

2、测试

十一、自定义异常

1、创建自定义异常类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class GuliException extends RuntimeException {
     @ApiModelProperty(value = "状态码")
     private Integer code;
     private String msg;
 
}

2、业务中需要的位置抛出GuliException

try {
     int a = 10/0;
}catch(Exception e) {
     throw new GuliException(20001,"出现自定义异常");
}

3、添加异常处理方法

GlobalExceptionHandler.java中添加

@ExceptionHandler(GuliException.class)
@ResponseBody
public R error(GuliException e){
     e.printStackTrace();
     return R.error().message(e.getMsg()).code(e.getCode());
}

4、测试

十二、日志

1、配置日志级别

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

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

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

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

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

十三、Logback日志

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

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

logback相对于log4j的一些优点:https://blog.csdn.net/caisini_vc/article/details/48551287

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="E:/guli_1010/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>

2、将错误日志输出到文件

GlobalExceptionHandler.java 中

类上添加注解

@Slf4j

异常输出语句

log.error(e.getMessage());

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

定义工具类

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值