如何优雅的将业务逻辑和日志及异常的处理进行解耦。一下是我个人的理解和处理方法。欢迎有更好idea的朋友留言探讨。
将业务逻辑和非业务逻辑的代码进行分离,一个常用的方法就是AOP。废话不多说,直接看代码。
先说日志的处理。首先,定义一个注释类:
/** * Author : Nathan.yang * Date : 2017/12/6 * Email : [email protected] */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD,ElementType.TYPE}) public @interface Log { }
/** * Author : Nathan.Yang * Date : 2017/12/6 * Email : [email protected] */ @Aspect @Order(1) @Component public class WebLogAspect { Logger logger = LoggerFactory.getLogger(this.getClass()); ThreadLocal<Long> startTime = new ThreadLocal<Long>(); //定义切入点 该包下的所有函数 @Pointcut("@annotation(cn.com.nathanyang.nathan_tool.log.Log)") public void webLog(){} @Before("webLog()") public void doBefore(JoinPoint joinPoint) throws Throwable { startTime.set(System.currentTimeMillis()); // 接收到请求,记录请求内容 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); // 记录下请求内容 logger.info("AOP URL : " + request.getRequestURL().toString()); logger.info("AOP HTTP_METHOD : " + request.getMethod()); logger.info("AOP IP : " + request.getRemoteAddr()); logger.info("AOP CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); logger.info("AOP ARGS : " + Arrays.toString(joinPoint.getArgs())); } @AfterReturning(returning = "ret", pointcut = "webLog()") public void doAfterReturning(Object ret) throws Throwable { // 处理完请求,返回内容 logger.info("AOP RESPONSE : " + JSON.toJSONString(ret)); logger.info("AOP SPEND TIME : {}ms", (System.currentTimeMillis() - startTime.get())); }
doBefore方法会在调用的目标方法运行前被调用。doBefore方法里面可以打印出一些目标方法相关的一些信息,如:请求路径,方法名,参数等。
doAfterReturning方法会在目标方法运行结束后被调用。这个方法里面可以打印出目标方法的运行结果和统计这个方法的耗时。
日志的处理这样就差不多完工了。
接下来就是对异常的统一处理了。同样我们也可以使用AOP对方法的异常进行捕获。不过在此之前,我们需要对异常类和异常编码进行处理。
首先是Result类,这个类是接口返回的规范类,包括三个重要部分:code(操作成功或者失败的编码)msg(操作结果的附带描述) ext(操作可能会携带的返回信息)。
/** * Author : Nathan.yang * * Date : 2018/1/23 * Email : [email protected] */ public class Result<T> implements Serializable { private String code; private String msg; private T ext;
有了Result后,我们就可以设计ResultCode了。在ResultCode中定义常用的变量。
/** * Author : Nathan.yang * * Date : 2018/1/23 * Email : [email protected] */ public class ResultCode { private String code; private String msg; public final static String SUCCESSCODE = "0"; public final static String FAILCODE = "-1"; private final static ResultCode SUCCESS = new ResultCode(SUCCESSCODE,"SUCCESS"); private final static ResultCode FAIL = new ResultCode(FAILCODE,"FAIL");
有了ResultCode,接下来是根据ResultCode来定义我们的异常编码信息。考虑到不同模块分开开发,所以对每个模块对ResultCode进行封装,并定义好自己的异常编码前缀。这样就不会出现编码重复问题。
/** * Author : Nathan.Yang * Date : 2018/2/24 * Email : [email protected] */ public class SysCode extends ResultCode { private static String pre = "00"; public SysCode(String code, String msg) { super(pre + code, msg); } }
/** * Author : Nathan.Yang * Date : 2018/2/24 * Email : [email protected] */ public class SysCodeMap { public static final SysCode SYS_ERROR = new SysCode("001","系统异常"); }
对异常类的封装
/** * Author : nathan.yang * Date : 2017/11/15. * Email : [email protected] */ public class BaseException extends RuntimeException { private ResultCode resultCode; public BaseException(ResultCode resultCode) { super(resultCode.getMsg()); this.resultCode = resultCode; } public ResultCode getResultCode() { return resultCode; } public void setResultCode(ResultCode resultCode) { this.resultCode = resultCode; } }
好了,准备工作完成,接下来是对异常的捕获及处理。
@Around("webLog()") public Object doException(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ try{ return proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs()); }catch (Exception e){ Result result; if (e instanceof BaseException){ ResultCode resultCode = ((BaseException)e).getResultCode(); result = Result.fail(resultCode); logger.warn("自定义异常:{}",resultCode.getMsg()); }else if (e instanceof HttpRequestMethodNotSupportedException){ result = Result.fail(ResultCode.fail(e.getMessage())); logger.warn("HttpRequestMethodNotSupportedException:{}",e.getMessage()); }else if (e instanceof MethodArgumentNotValidException){ List<ObjectError> list = ((MethodArgumentNotValidException)e).getBindingResult().getAllErrors(); StringBuffer buffer = new StringBuffer(); list.forEach((error) -> { if(error instanceof FieldError) { buffer.append(((FieldError)error).getField()).append(":"); } buffer.append(error.getDefaultMessage()).append(","); }); result = Result.fail(ResultCode.fail(buffer.toString())); logger.warn("参数异常:{}",e.getMessage()); }else if (e instanceof HttpMessageNotReadableException){ result = Result.fail(ResultCode.fail("please check your request body!")); logger.warn("参数异常:{}",e.getMessage()); }else if (e instanceof MethodArgumentTypeMismatchException){ result = Result.fail(ResultCode.fail("please check your parameter type!")); logger.warn("参数异常:{}",e.getMessage()); }else{ result = Result.fail(ResultCode.fail()); logger.warn("系统异常:{}",e.getMessage()); } return result; } }
使用Around来捕获异常,如果是自定义的异常,从异常中获取ResultCode来构建Result并返回。如果不是自定义异常,根据异常信息构建Result并返回。
接下来看看如何使用。
/** * 注册 * @param account * @param pw * @return */ @PostMapping("/user/register") @ValidateGroup(fileds = { @ValidateField(index = 0,notNull = true), @ValidateField(index = 1,notNull = true) }) @Log public Result register(String account,String pw){ return Result.success(userService.register(account, pw)); }
@Override public User register(String account, String pw) { //查重 User user = userRepository.findByAccount(account); if (null != user){ throw new BaseException(PermissionCodeMap.USER_EXIST); }
以上就是对Java的日志及异常处理的统一分离处理。
原创文章,转载请注释来处,谢谢!