统一返回对象
package com.demo1.rsponse;
public class Result<T> {
/** 错误码. */
private Integer code;
/** 提示信息. */
private String msg;
/** 具体的内容. */
private T data;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
复制代码
package com.demo1.response;
import com.demo1.response.Result;
public class ResultUtil {
public static Result success(Object object) {
Result result = new Result();
result.setCode(1);
result.setMsg("成功");
result.setData(object);
return result;
}
public static Result success() {
return success(null);
}
public static Result error(Integer code, String msg) {
Result result = new Result();
result.setCode(code);
result.setMsg(msg);
return result;
}
}
复制代码
统一异常信息及处理
我们想要统一异常处理,首先,需要定义一个自己的异常类来统一对外的异常展示;异常展示一般需要错误码errCode和错误信息errMsg,为了方便统一管理它们,我们会再来一个枚举类来定义它们;最后我们再来一个类捕获并统一处理异常,这样就能比较完美的实现异常的统一处理。在业务逻辑的实现中使用的话就是直接抛我们自己定义的异常。
下面来一个demo,自定义类继承了RuntimeException,统一异常处理使用了Spring的@ControllerAdvice和@ExceptionHandler注解来实现,并通过日志记录下非运行时异常信息。
自定义异常类
import com.demo1.error.EmBusinessError;
public class BusinessException extends RuntimeException{//使用RuntimeException的原因为spring框架只对抛出的RuntimeException异常进行事务回滚
private Integer code;
public BusinessException(EmBusinessError emBusinessError) {
super(emBusinessError.getMsg());
this.code = emBusinessError.getCode();
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
}
复制代码
事务回滚
在spring的事务管理环境下,使用unckecked exception(未检查异常)可以极大地简化异常的处理,只需要在事务层声明可能抛出的异常(这里的异常可以是自定义的unckecked exception体系),在所有的中间层都只是需要简单throws即可,不需要捕捉和处理,直接到最高层,比如UI层再进行异常的捕捉和处理
在service类前加上@Transactional,声明这个service所有方法需要事务管理。每一个业务方法开始时都会打开一个事务。
Spring默认情况下会对运行期例外(RunTimeException)进行事务回滚。这个例外是unchecked(未检查异常),如果遇到checked(检查异常)例外就不回滚。
如何改变默认规则:
1 让checked例外(检查异常)也回滚:在整个方法前加上 @Transactional(rollbackFor=Exception.class)
2 让unchecked例外(未检查异常)不回滚: @Transactional(notRollbackFor=RunTimeException.class)
3 不需要事务管理的(只查询的)方法:@Transactional(propagation=Propagation.NOT_SUPPORTED)
注意: 如果异常被try{}catch{}了,事务就不回滚了,如果想让事务回滚必须再往外抛try{}catch{throw Exception}。
放置错误码和错误信息的枚举类
public enum EmBusinessError {
UNKONW_ERROR(-1, "未知错误"),
SUCCESS(1, "成功"),
PRIMARY_SCHOOL(100, "你可能还在上小学"),
MIDDLE_SCHOOL(101, "你可能还在上初中"),
;
private Integer code;
private String msg;
EmBusinessError(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
复制代码
异常捕获处理类
import com.demo1.domain.Result;
import com.demo1.utils.ResultUtil;
import com.demo1.error.BusinessException;
import org.slf4.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice//异常集中处理,更好的使业务逻辑与异常处理剥离开
public class ExceptionHandle {
private final static Logger logger = LoggerFactory.getLogger(ExceptionHandle.class);
@ExceptionHandler(value = Exception.class)//统一处理指定类型异常,从而能够减少代码重复率和复杂度
@ResponseBody
public Result handle(Exception e) {
if (e instanceof BusinessException) {
BusinessException BusinessException = (BusinessException) e;
return ResultUtil.error(BusinessException.getCode(), BusinessException.getMessage());
}else {
logger.error("【系统异常】{}", e);
return ResultUtil.error(-1, "未知错误");
}
}
}
复制代码
slf4j
这里使用了slf4j提供的日志接口,简单介绍一下slf4j:
官方文档定义:Simple Logging Facade for Java(SLF4J)用作各种日志框架(例如java.util.logging,logback,log4j)的简单外观或抽象,允许最终用户在部署 时插入所需的日志记录框架。
slf4j只是一个日志标准
,并不是日志系统的具体实现。理解这句话非常重要,slf4j只做两件事情:
- 提供日志接口
- 提供获取具体日志对象的方法
slf4j-simple、logback都是slf4j的具体实现,log4j并不直接实现slf4j,但是有专门的一层桥接slf4j-log4j12来实现slf4j。
门面模式
slf4j是门面模式的典型应用,再简单介绍一下门面模式:
门面模式,隐藏系统的复杂性,并向客户端提供了一个可以访问系统的接口。这种类型的设计模式属于结构性模式,门面模式是对象的结构模式。为子系统中的一组接口提供了一个统一的访问接口,这个接口使得子系统更容易被访问或者使用。 其核心为外部与一个子系统的通信必须通过一个统一的外观对象进行,使得子系统更易于使用。用一张图来表示门面模式的结构为:
门面模式的核心为Facade即门面对象,门面对象核心为几个点:
- 知道所有子角色的功能和责任
- 将客户端发来的请求委派到子系统中,没有实际业务逻辑
- 不参与子系统内业务逻辑的实现
@ControllerAdvice和@ExceptionHandler注解
官方文档上对@ControllerAdvice的说法是作为@component的专门化,允许通过类路径扫描自动检测实现类。
它通常用于定义应用于所有@Requestmapping方法的@ExceptionHandler、@InitBinder和@ModelAttribute。意思是启动应用后,被 @ExceptionHandler、@InitBinder、@ModelAttribute 注解的方法,都会作用在被@RequestMapping 注解的方法上。
@ExceptionHandler 拦截异常,我们可以通过该注解实现自定义异常处理。其中,@ExceptionHandler 配置的 value 指定需要拦截的异常类型。