flylib-boot是针对springboot构建的程序的基础框架,专门用于构建程序里的比如统一 异常处理
包含一个Spring Boot的一些常见的基础组件的设置
- 针对Handler的全局的异常处理(处理所有Controller里的Handler里抛出的异常)
- Filter
- Interceptor
注意:SpringBoot(SpringMVC)里的Handler特指@Controller注解的类里的每个处理HTTP请求的一个public method.
- Step 1: 进入目录flylib-boot-starter,执行
mvn install
- Step 2: 在自己的项目中添加flylib-boot-starter的maven依赖. 并留意自己使用的spring-boot版本,去修改自己的pom.xml文件
<dependency>
<groupId>org.flylib</groupId>
<artifactId>flylib-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
并且要注意这里spring-boot版本是1.5.0.RELEASE. 另外需要添加spring-boot-maven-plugin 实例参考spring-boot-demo项目,它的pom如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.flylib</groupId>
<artifactId>flylib-boot-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.flylib</groupId>
<artifactId>flylib-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- Step 3: 在自己的程序中new 一个UserException(自定义的异常类)设置捕获异常
/**
* 用户信息的异常
*/
public class UserException extends RuntimeException{
}
@RequestMapping("")
public String index() throws RuntimeException {
UserException userException = new UserException();
CustomRuntimeException cause = new CustomRuntimeException("001", "User not exists");
userException.initCause(cause);
throw userException;
}
- Step 4: 运行自己的Spring Boot项目 输出到浏览器的结果
{
code:"001",
message:"User not exists",
throwable:{...}
}
利用了@ControllerAdvice和@ExceptionHandler 实现代码是
package org.flylib.boot.starter.handler;
import org.flylib.boot.starter.exception.CustomRuntimeException;
import org.flylib.boot.starter.exception.UnknownResourceException;
import org.flylib.boot.starter.exception.ValidationRuntimeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.ui.Model;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
/**
* 说明:
*
* @ControllerAdvice是controller的一个辅助类,最常用的就是作为全局异常处理的切面类
* @ControllerAdvice可以指定扫描范围
* @ControllerAdvice约定了几种可行的返回值,如果是直接返回model类的话,需要使用@ResponseBody进行json转换 返回String,表示跳到某个view
* 返回modelAndView
* 返回model + @ResponseBody
* 全局异常处理
*/
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@Autowired
private Environment env;
@Autowired(required = false)
private MessageSource messageSource;
@Autowired(required = false)
private LocaleResolver localeResolver;
private static final String defaultMoreInfoUrl = "";
private final Map<String,HttpStatus> DEFAULT_EXCEPTION_MAPPING_DEFINITIONS;
public GlobalExceptionHandler() {
DEFAULT_EXCEPTION_MAPPING_DEFINITIONS = createDefaultExceptionMappingDefinitions();
}
@ExceptionHandler//处理所有异常
@ResponseBody //在返回自定义相应类的情况下必须有,这是@ControllerAdvice注解的规定
public Map<String,Object> exceptionHandler(Throwable e, HttpServletRequest request, HttpServletResponse response, Model model) {
//
log.error("handle error:",e);
HttpStatus httpStatus = DEFAULT_EXCEPTION_MAPPING_DEFINITIONS.get(e.getClass().getName());
if(httpStatus==null){
httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
}
//是否是生产环境
boolean isProd = "prod".equals(env.getActiveProfiles()[0]);
Map<String,Object> map = new HashMap<String,Object>();
if(e.getCause() instanceof CustomRuntimeException){
CustomRuntimeException exception = (CustomRuntimeException) e.getCause();
map.put("code",String.valueOf(exception.getCode()));
map.put("message",exception.getMessage());
}else if(e.getCause() instanceof ValidationRuntimeException){
ValidationRuntimeException exception = (ValidationRuntimeException) e.getCause();
map.put("code",String.valueOf(exception.getCode()));
map.put("message",exception.getMessage());
httpStatus = HttpStatus.BAD_REQUEST;
}else {
map.put("code",String.valueOf(httpStatus.value()));
map.put("message",httpStatus.toString());
}
//不是生产环境,添加调试信息
if(!isProd){
map.put("throwable",e);
}
response.setStatus(httpStatus.value());
return map;
}
protected final Map<String,HttpStatus> createDefaultExceptionMappingDefinitions() {
Map<String,HttpStatus> m = new LinkedHashMap<String, HttpStatus>();
// 400
applyDef(m, HttpMessageNotReadableException.class, HttpStatus.BAD_REQUEST);
applyDef(m, MissingServletRequestParameterException.class, HttpStatus.BAD_REQUEST);
applyDef(m, TypeMismatchException.class, HttpStatus.BAD_REQUEST);
applyDef(m, "javax.validation.ValidationException", HttpStatus.BAD_REQUEST);
// 404
applyDef(m, NoSuchRequestHandlingMethodException.class, HttpStatus.NOT_FOUND);
applyDef(m, "org.hibernate.ObjectNotFoundException", HttpStatus.NOT_FOUND);
// 405
applyDef(m, HttpRequestMethodNotSupportedException.class, HttpStatus.METHOD_NOT_ALLOWED);
// 406
applyDef(m, HttpMediaTypeNotAcceptableException.class, HttpStatus.NOT_ACCEPTABLE);
// 409
//can't use the class directly here as it may not be an available dependency:
applyDef(m, "org.springframework.dao.DataIntegrityViolationException", HttpStatus.CONFLICT);
// 415
applyDef(m, HttpMediaTypeNotSupportedException.class, HttpStatus.UNSUPPORTED_MEDIA_TYPE);
applyDef(m, UnknownResourceException.class, HttpStatus.NOT_FOUND);
return m;
}
private void applyDef(Map<String,HttpStatus> m, Class clazz, HttpStatus status) {
applyDef(m, clazz.getName(), status);
}
private void applyDef(Map<String,HttpStatus> m, String key, HttpStatus status) {
m.put(key, status);
}
protected String getMessage(String msg, ServletWebRequest webRequest, Exception ex) {
if (msg != null) {
if (msg.equalsIgnoreCase("null") || msg.equalsIgnoreCase("off")) {
return null;
}
msg = ex.getMessage();
if (messageSource != null) {
Locale locale = null;
if (localeResolver != null) {
locale = localeResolver.resolveLocale(webRequest.getRequest());
}
msg = messageSource.getMessage(msg, null, msg, locale);
}
}
return msg;
}
}