Spring Boot统一异常处理

Java异常分为非检查异常(Error和RuntimeException以及它们的子类)以及检查异常(除Error 和 RuntimeException的其它异常),检查异常需要我们显式地进行try,catch处理或者throws,而非检查异常则不需要。
程序中出现异常通常是我们不希望看到的,但合理地定义和处理Java异常,却可以给编程带来很大的便利。比如,合理地定义业务异常,结合Spring提供的声明式事务,在需要的地方抛出RuntimeException异常可以让事务进行回滚,更重要的是,它可以方便的将我们的业务代码以及异常处理的代码分离开来,让代码看起来简洁的同时,也易于理解和维护。
下面是Spring Boot中使用全局异常处理的一个简单的例子:

1 使用枚举类定义我们项目中会出现的业务异常,包括业务错误码和错误提示信息:

public enum ServiceResultEnum {
    SUCCESS(0,"操作成功"),
    REQUEST_PARAM_ERROR(200,"请求参数错误"),
    USER_NOT_EXIST(100,"用户不存在"),
    UNKONWN_ERROR(-1,"系统错误");

    private Integer code;//错误码
    private String message;//错误信息

    private ServiceResultEnum(Integer code, String message) {
        this.code = code;
        this.message = message;
    }

    public Integer getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }
}

2 定义我们的通用业务异常类(继承至RuntimeException),用前面定义的业务异常枚举类标识我们业务中抛出的每一个异常:

public class ServiceException extends RuntimeException {

    private Integer code;//错误码

    public ServiceException(ServiceResultEnum result) {
        super(result.getMessage());
        this.code = result.getCode();
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }
}

3 统一ajax返回结果:

public class ResultDTO<T> {
    private boolean success;//请求是否成功
    private Integer code;//返回状态码
    private String message;//返回消息
    private T data;//返回数据

    /**
     * 无参构造函数
     */

    public ResultDTO() {
        super();
    }

    /**
     * 构造函数
     * @param success
     */
    public ResultDTO(boolean success) {
        super();
        this.success = success;
    }

    public boolean isSuccess() {
        return success;
    }
    public void setSuccess(boolean success) {
        this.success = success;
    }
    public Integer getCode() {
        return code;
    }
    public void setCode(Integer code) {
        this.code = code;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public T getData() {
        return data;
    }
    public void setData(T data) {
        this.data = data;
    }
}

4 定义通用工具类,快速构造返回结果:

    /**
     * 
     * @Description: 构造成功结果
     * @param object
     * @return
     */
    public static ResultDTO success(Object data) {
        ResultDTO result = new ResultDTO(true);
        result.setCode(ServiceResultEnum.SUCCESS.getCode());
        result.setMessage(ServiceResultEnum.SUCCESS.getMessage());
        result.setData(data);
        return result;
    }

    /**
     * 构造成功返回结果
     * @return
     */
    public static ResultDTO success() {
        ResultDTO result = new ResultDTO(true);
        result.setCode(ServiceResultEnum.SUCCESS.getCode());
        result.setMessage(ServiceResultEnum.SUCCESS.getMessage());
        return result;
    }

    /**
     * 
     * @Description: 构造失败结果 
     * @param code
     * @param message
     * @return
     */
    public static ResultDTO error(Integer code,String message) {
        ResultDTO result = new ResultDTO(false);
        result.setCode(code);
        result.setMessage(message);
        return result;
    }
}

5 定义全局异常处理,这里我们定义一个工具类AjaxUtil,用来判断请求是否ajax为ajax请求,并以此确定应该进行返回页面资源还是直接响应流写回返回结果:

public class AjaxUtil {

    private  static  ObjectMapper mapper = new ObjectMapper();

    /**
     * 
     * @Description:  判断请求是否Ajax请求
     * @param request
     * @return
     */
    public static boolean isAjaxRequest(HttpServletRequest request) {
        String requestHeader = request.getHeader("X-Requested-With");
        return requestHeader != null ? "XMLHttpRequest".equals(requestHeader) : false;
    }

    /**
     *
     * @Description: Ajax请求时,往页面回写Json对象
     * @param value
     * @param response
     */
    public static  void writeJsonObject(Object value, HttpServletResponse response){
        JsonGenerator jsonGenerator = null;
        try {
            jsonGenerator=mapper.getFactory().createGenerator(response.getOutputStream(), JsonEncoding.UTF8);
            if(jsonGenerator!=null){
                jsonGenerator.writeObject(value);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

public class BaseExceptionHandler {

    /**
     * 
     * @Description:处理普通页面请求
     * @param url 跳转的页面
     * @param errorMessage
     * @param viewName
     * @return
     */
    protected ModelAndView handleViewException(String url, String errorMessage, String viewName) {        
        ModelAndView mav = new ModelAndView();
        //添加错误页面数据模型
        mav.addObject("redirectUrl", url);     
        mav.addObject("errorMessage", errorMessage);  
        mav.setViewName(viewName);    

        return mav;   
    }

    /**
     * 
     * @Description: 处理Ajax请求
     * @param response
     * @param code
     * @param execptionMessage
     * @return
     */
    protected ModelAndView handleAjaxException(HttpServletResponse response,
            Integer code,String execptionMessage){
        response.setContentType("text/xml;charset=utf-8");
        AjaxUtil.writeJsonObject(ResultDTOUtil.error(code, execptionMessage), response);
        return null;    
    }
}

@ControllerAdvice
public class GlobalExceptionHandler extends BaseExceptionHandler{
    /**
     * 日志记录
     */
    private final static Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 
     * 捕捉并处理404异常
     * @param e
     * @return
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ModelAndView handle404Error(Exception e) {
         return new ModelAndView("error/404");
    }

    /**
     * 捕捉业务异常
     * @param request
     * @param response
     * @param e
     * @return
     */
    @ExceptionHandler(ServiceException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ModelAndView handlServiceException(HttpServletRequest request, HttpServletResponse response, Exception e) {
        logger.error(e.getMessage(), e);
        ServiceException servicException = (ServiceException)e;
        int code = servicException.getCode();
        String msg = servicException.getMessage();
        //根据错误码进行判断属于ajax异常还是页面异常
        if(AjaxUtil.isAjaxRequest(request)){
            return handleAjaxException(response, code, msg);
        }else{
            String viewName = "error/default";
            //可根据自定义的具体错误码跳转到相应的错误页面
            return handleViewException(request.getRequestURL().toString(),msg,viewName);
        }
    }

    /**
     * 其他异常处理
     * @param request
     * @param response
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ModelAndView handleSysException(HttpServletRequest request, HttpServletResponse response, Exception e){
        logger.error(e.getMessage(),e);
        if(AjaxUtil.isAjaxRequest(request)){
            return handleAjaxException(response,
                    ServiceResultEnum.UNKONWN_ERROR.getCode(),ServiceResultEnum.UNKONWN_ERROR.getMessage());
        }else{
            String viewName = "error/default";
            return handleViewException(request.getRequestURL().toString(),
                    ServiceResultEnum.UNKONWN_ERROR.getMessage(),viewName);
        }
    }

}

6 模拟业务异常,这里为方便,省略了业务类的编写:

@RestController
public class UserController {

    private static Map<String,User> users = new ConcurrentHashMap<>();

    @PostMapping("/users")
    public ResultDTO add(User user){
        if(user == null ||
                user.getId() == null ||
                    user.getName() == null){
            throw new ServiceException(ServiceResultEnum.REQUEST_PARAM_ERROR);
        }
        users.put(user.getName(),user);
        return ResultDTOUtil.success();
    }

    @GetMapping("/users/{id}")
    public ResultDTO findOne(@PathVariable(name = "id")String id) throws Exception {
        if(!users.containsKey(id)){
            throw new ServiceException(ServiceResultEnum.USER_NOT_EXIST);
        }
        return ResultDTOUtil.success(users.get(id));
    }

    @DeleteMapping("users/{id}")
    public ResultDTO delete(@PathVariable(name = "id")String id){
        if(!users.containsKey(id)){
            throw new ServiceException(ServiceResultEnum.USER_NOT_EXIST);
        }
        users.remove(id);
        return ResultDTOUtil.success();
    }

    @PutMapping("users/{id}")
    public ResultDTO update(@PathVariable(name = "id")String id,User user){
        if(!users.containsKey(id)){
            throw new ServiceException(ServiceResultEnum.USER_NOT_EXIST);
        }
        if(user != null &&
                StringUtils.isNotEmpty(user.getName())){
            User old = users.get(id);
            old.setName(user.getName());
        }
        return ResultDTOUtil.success(users.get(id));
    }
}

关于异常的设计和处理,这里推荐一篇不错的文章:文章链接

猜你喜欢

转载自blog.csdn.net/CrazyLai1996/article/details/82120543
今日推荐