一、目的:
在分布式系统中,定义一个全局异常处理器stater,其它微服务只需要引入依赖即可。
为了方便维护错误信息,因此将错误信息封装到一个枚举中。
为了封装异常处理代码,因此将所有接口的异常处理封装到了springmvc的 ControllerAdvice 中。
二、代码实现:
1、引入依赖:
<!--spring的全局异常处理器也是一个controller,需要相关依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
2、新建一个接口:
public interface IException {
public int getCode();
public String getMessage();
}
3、新建一个枚举类:
@NoArgsConstructor
@AllArgsConstructor
public enum ExceptionEnum implements IException {
NOT_FIND(404, "查询失败"),
;
int code;
String message;
@Override
public int getCode() {
return code;
}
@Override
public String getMessage() {
return message;
}
}
4、新建一个自定义异常类:
error方法可同时记录日志和向上抛异常
@Data
public class MyException extends RuntimeException {
private Integer errorCode;
private String errorMessage;
public MyException (IException iException) {
this.errorCode = iException.getCode();
this.errorMessage = iException.getMessage();
}
public static void error(Logger logger, IException e,Exception source) {
logger.error("异常码[{}],异常提示[{}],异常详情:",
e.getCode(), e.getMessage(),source);
throw new MyException (e);
}
public static void error(Logger logger, IException e) {
logger.error("异常码[{}],异常提示[{}]",
e.getCode(), e.getMessage());
throw new MyException (e);
}
}
5、全局异常处理器(处理逻辑较为简单,仅供参考):
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
@ExceptionHandler(value = Exception.class)
public ResponseEntity<Map<String,Object>> exceptionHandler(Exception e, HttpServletRequest request){
log.error("Global Exception Occured => url : {}", request.getRequestURL());
//打印完成错误信息,方便调试
e.printStackTrace();
//检测是否是自定义异常,如果是响应详细信息
if(e instanceof EgoException){
EgoException exception=(EgoException) e;
//利用Map实现VO(也可以自定义VO)
Map<String, Object> resultVO = new HashMap<>(2);
resultVO.put("errorCode", exception.getErrorCode());
resultVO.put("errorMessage", exception.getErrorMessage());
//将具体错误信息响应给客户端
return ResponseEntity.status(exception.getErrorCode()).body(resultVO);
}
// 如果不是,直接返回500
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
6、新增自动配置文件GlobalExceptionHandlerAutoConfiguration
@Configuration
public class GlobalExceptionHandlerAutoConfiguration {
@Bean
public GlobalExceptionHandler globalExceptionHandler() {
return new GlobalExceptionHandler();
}
}
7、新增resouces/META-INF/spring.factories,内容如下:
#值为自定义的自动配置文件路径
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.test.exception.GlobalExceptionHandlerAutoConfiguration
8、记录日志:
引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
新建文件logback-spring.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<!--输出日志格式-->
<appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>
<!--%d - %msg%n-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</pattern>
</layout>
</appender>
<!--只保存info日志-->
<appender name="fileInfoLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<encoder>
<pattern>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</pattern>
</encoder>
<!--滚动输出策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--路径-->
<fileNamePattern>d:/logs/info/info.%d.log</fileNamePattern>
</rollingPolicy>
</appender>
<!--只保存warn日志-->
<appender name="fileWarnLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<encoder>
<pattern>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</pattern>
</encoder>
<!--滚动输出策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--路径-->
<fileNamePattern>d:/logs/warn/warn.%d.log</fileNamePattern>
</rollingPolicy>
</appender>
<!--只保存error日志-->
<appender name="fileErrorLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
<!--滚动输出策略-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--路径-->
<fileNamePattern>d:/logs/error/error.%d.log</fileNamePattern>
</rollingPolicy>
</appender>
<root level="info">
<appender-ref ref="consoleLog"/>
<appender-ref ref="fileInfoLog"/>
<appender-ref ref="fileWarnLog"/>
<appender-ref ref="fileErrorLog"/>
</root>
</configuration>
9、使用时,在其它模块中引入此模块依赖,业务代码如下:
try{
...
}
catch (Exception e) {
MyException.error(log,ExceptionEnum.BRAND_NOT_FOUND,e);
}