一、引入依赖
在SpringBoot中引入AOP是一件很方便的事,和其他引入依赖一样,我们只需要在POM中引入starter就可以了:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
二、自定义异常信息枚举类
这里可以自定义服务中的一些异常信息
/**
* 异常信息枚举类
*
* @author renjie
* @version 1.0.0
*/
public enum ExceptionEnum {
SYSTEM_ERROR("500","系统异常"),
PARAM_ERROR("231","参数错误");
private String code;
private String msg;
ExceptionEnum(String code, String msg) {
this.code = code;
this.msg = msg;
}
public String getCode() {
return code;
}
public String getMsg() {
return msg;
}
public void setCode(String code) {
this.code = code;
}
public void setMsg(String msg) {
this.msg = msg;
}
二、自定义错误信息类
一般系统抛出的错误是不含错误代码的,除去部分的404,400,500错误之外,我们如果想把错误代码定义的更细致,就需要自己继承RuntimeException这个类后重新定义一个构造方法来定义我们自己的错误信息:
/**
* 自定义错误信息类
*
* @author renjie
* @version 1.0.0
*/
public class DescribeException extends RuntimeException{
private String code;
/**
* 继承exception,加入错误状态值
* @param exceptionEnum
*/
public DescribeException(ExceptionEnum exceptionEnum) {
super(exceptionEnum.getMsg());
this.code = exceptionEnum.getCode() + "";
}
/**
* 自定义错误信息
* @param message
* @param code
*/
public DescribeException(String message, String code) {
super(message);
this.code = code;
}
/**
* 自定义错误信息
* @param e
*/
public DescribeException(Exception e){
super(e.getMessage());
this.code = ExceptionEnum.SYSTEM_ERROR.toString();
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
三、自定义返回类
在上一篇SpringBoot快速开发(四)【Controller层】贴出的代码中,控制层返回的类型为:ObjectResult,该类即为自定义的返回类型,内容如下。注意到其中的几个静态方法:success、error等,这些方法可直接在Controller层封装成功或错误信息,并返回。
/**
* 返回的对象
*
* @author renjie
* @version 1.0.0
*/
public class ObjectResult<T> implements Serializable {
private static final long serialVersionUID = -9146805371831100892L;
final public static ObjectResult SUCCESS = new ObjectResult("200", "操作成功");
private String code;
private String msg;
private T data;
public ObjectResult() {
}
public ObjectResult(String code, String msg) {
this(code, msg, null);
}
public ObjectResult(String code, String msg, T data) {
this.code = code;
this.msg = msg;
this.data = data;
}
private ObjectResult copyThis() {
return new ObjectResult(code, msg, null);
}
/**
* 操作成功(有返回数据)
* @param object
* @return
*/
public static ObjectResult success(Object object) {
ObjectResult r = ObjectResult.SUCCESS.copyThis();
r.setData(object);
return r;
}
/**
* 操作成功(无返回数据)
* @return
*/
public static ObjectResult success() {
ObjectResult r = ObjectResult.SUCCESS.copyThis();
r.setData(null);
return r;
}
/**
* 自定义错误信息
* @param describeException
* @return
*/
public static ObjectResult error(DescribeException describeException){
ObjectResult objectResult = new ObjectResult();
objectResult.setCode(describeException.getCode());
objectResult.setMsg(describeException.getMessage());
return objectResult;
}
/**
* 返回异常信息,在已知的范围内
* @param exceptionEnum
* @return
*/
public static ObjectResult error(ExceptionEnum exceptionEnum){
ObjectResult objectResult = new ObjectResult();
objectResult.setCode(exceptionEnum.getCode());
objectResult.setMsg(exceptionEnum.getMsg());
return objectResult;
}
public String getCode() {
return code;
}
public void setCode(String 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;
}
}
四、异常捕获并处理
上面已经创建了异常枚举类、错误信息封装类、自定义返回类,还需要最重要的异常信息捕获类。
Spring为我们提供了两个比较好的注解:
- @ExceptionHandler:统一处理某一类异常,从而能够减少代码重复率和复杂度
- @ControllerAdvice:异常集中处理,更好的使业务逻辑与异常处理剥离开
/**
* 异常信息处理类
*
* @author renjie
* @version 1.0.0
*/
@ControllerAdvice
public class ExceptionHandle {
private final static Logger log = LoggerFactory.getLogger(ExceptionHandle.class);
/**
* 判断错误是否是已定义的已知错误,不是则由未知错误代替,同时记录在log中
* @param e
* @return
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public ObjectResult exceptionGet(Exception e){
if(e instanceof DescribeException){
DescribeException MyException = (DescribeException) e;
return ObjectResult.error(MyException);
}
log.error("【系统异常】{}",e);
return ObjectResult.error(ExceptionEnum.SYSTEM_ERROR);
}
}
到这里为止,我们已经完成了对结果以及异常的统一返回管理,并且在出现异常时,我们可以不返回错误信息给前端,而是用未知错误进行代替,只有查看log我们才会知道真实的错误信息。
五、AOP对接口调用进行管理
我们使用接口若出现了异常,很难知道是谁调用接口,是前端还是后端出现的问题导致异常的出现,那这时,AOP久发挥作用了,我们之前已经引入了AOP的依赖,现在我们编写一个切面类,切点如何配置不需要我多说了吧:
/**
* 切面配置信息
*
* @author renjie
* @version 1.0.0
*/
@Aspect
@Component
public class HttpAspect {
private final static Logger log = LoggerFactory.getLogger(HttpAspect.class);
@Pointcut("execution(public * com.rj.demo.test.controller.*.*(..))")
public void log(){
}
@Before("log()")
public void doBefore(JoinPoint joinPoint){
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//url
log.info("url={}",request.getRequestURL());
//method
log.info("method={}",request.getMethod());
//ip
log.info("id={}",request.getRemoteAddr());
//class_method
log.info("class_method={}",joinPoint.getSignature().getDeclaringTypeName() + "," + joinPoint.getSignature().getName());
//args[]
log.info("args={}",joinPoint.getArgs());
}
@Around("log()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
return proceedingJoinPoint.proceed();
}
@AfterReturning(pointcut = "log()",returning = "object")//打印输出结果
public void doAfterReturing(Object object){
if(CommUtil.isNotNullOrEmpty(object)){
log.info("response={}",object.toString());
}
}
}
六、异常管理测试
接下来我们心新写一个ResultController来测试一下:
/**
* 控制层
*
* @author renjie
* @version 1.0.0
*/
@RestController
@RequestMapping("/demo")
public class Controller {
@Autowired
Rj rj;
@RequestMapping(value="/test", method = RequestMethod.GET)
public ObjectResult test(String id){
if(CommUtil.isNullOrEmpty(id)){
throw new DescribeException(ExceptionEnum.PARAM_ERROR);
}
String name = rj.getName();
return ObjectResult.success(name + ":" + id);
}
}
异常的情况:
正常的情况: