实际开发过程中会遇到对dao层、service层和controller层抛出的异常统一处理,对每个模块的方法进行异常处理也能解决问题,但这样是不合理的,并且代码看起来也不雅观,这时就可以使用@ExceptionHandler对Web层进行统一异常处理,让异常由下往上(dao—>service—>controller)抛出。
代码如下:
import com.sand.base.core.common.BaseCommon;
import com.sand.base.core.entity.ResultEntity;
import com.sand.base.enums.ResultEnum;
import com.sand.base.exception.LsException;
import com.sand.base.util.ResultUtil;
import com.sand.base.util.editor.DateEditor;
import com.sand.base.util.editor.StringEditor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Date;
import java.util.Objects;
/**
* 功能说明:Web层通用数据处理
* 开发人员:@author liusha
* 开发日期:2019/8/26 21:43
* 功能描述:定义所有控制器的父控制器,进行属性绑定、数据转换、异常处理
*/
@Slf4j
public class BaseController extends BaseCommon {
protected HttpSession session;
protected HttpServletRequest request;
protected HttpServletResponse response;
/**
* 属性访问器
*
* @param session session
* @param request request
* @param response response
*/
@ModelAttribute
public void modelAttribute(HttpSession session, HttpServletRequest request, HttpServletResponse response) {
this.session = session;
this.request = request;
this.response = response;
}
/**
* 属性编辑器
*
* @param binder 数据绑定
*/
@InitBinder
public void initBinder(WebDataBinder binder) {
// Date类型转换,几乎支持所有的日期类型
binder.registerCustomEditor(Date.class, new DateEditor());
// String类型转换,将所有传递进来的String进行HTML编码,防止XSS攻击
binder.registerCustomEditor(String.class, new StringEditor());
}
/**
* 顶级的异常处理
*
* @param e 异常
* @return 响应客户端
*/
@ExceptionHandler(Exception.class)
public ResultEntity handleException(Exception e) {
errorLog(e);
return ResultUtil.error();
}
/**
* 自定义异常处理
*
* @param e 异常
* @return 响应客户端
*/
@ExceptionHandler(LsException.class)
public ResultEntity handleLsException(LsException e) {
errorLog(e);
return ResultUtil.info(e.getCode(), e.getMessage());
}
/**
* 参数缺失处理
*
* @param e 异常
* @return 响应客户端
*/
@ExceptionHandler(MissingServletRequestParameterException.class)
public ResultEntity handleMissingParamException(MissingServletRequestParameterException e) {
errorLog(e);
return ResultUtil.error(ResultEnum.PARAM_MISSING_ERROR);
}
/**
* json反序列化处理
*
* @param e 异常
* @return 响应客户端
*/
@ExceptionHandler(HttpMessageNotReadableException.class)
public ResultEntity handleMessageNotReadableException(HttpMessageNotReadableException e) {
errorLog(e);
return ResultUtil.error(ResultEnum.JSON_DESERIALIZE_ERROR);
}
/**
* 打印出错log
*
* @param e 异常
*/
private void errorLog(Exception e) {
StackTraceElement element = e.getStackTrace()[0];
if (e instanceof LsException) {
log.info("异常位置:{}.{},第{}行,原因:{}", element.getClassName(), element.getMethodName(), element.getLineNumber(), e.getMessage());
if (!Objects.isNull(e.getCause())) {
StackTraceElement cause = e.getCause().getStackTrace()[0];
log.info("起因:{}.{},第{}行,原因:{}", cause.getClassName(), cause.getMethodName(), cause.getLineNumber(), e.getCause().getMessage());
}
} else {
log.error("错误位置:{}.{},第{}行,错误原因:{}", element.getClassName(), element.getMethodName(), element.getLineNumber(), e.getClass().getName());
}
log.error("错误信息:" + e);
}
}
我这里只列出了其中几种异常,异常种类远远不止这些,可以参考API文档进行选择自己需要的异常http://tool.oschina.net/apidocs/apidoc?api=Spring-3.1.1
需要注意的是,这种处理方式只对当前controller有效,也就是只有当controller继承了BaseController才能捕获到异常。如果希望没有继承BaseController的controller也能进行异常捕获,就需要新建一个@ControllerAdvice切面类配合@ExceptionHandler进行全局异常处理。