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));
}
}
关于异常的设计和处理,这里推荐一篇不错的文章:文章链接