写点什么

以闭包方式实现一个 Controller 层打印日志的切面

作者:LSJ
  • 2021 年 11 月 10 日
  • 本文字数:2346 字

    阅读完需:约 8 分钟

一开始的时候关于这个切面的定义非常简单:拦截所有 Controller 层调用,打印输入参数和返回值。around 方法可以这么写:


@Around("writeLog()")public Object aroundMethod(ProceedingJoinPoint joinPoint) {    String methName= joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();    Object[] args = joinPoint.getArgs();    String[] parameterNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();    StringBuilder sb = new StringBuilder();    //拼接日志相关代码 ...    log.info(sb.toString());    try {        final Object proceed = joinPoint.proceed();        final String s = JSONObject.toJSONString(joinPoint.proceed());        log.info("方法路径: {} 返回值: {}", methName, s);        return proceed;    } catch (Throwable e) { e.printStackTrace(); }    return Result.error();}
复制代码


这么做有一个缺陷就是不能区分对待,没法指定哪个方法可以不打印日志,为了使之使用起来比较灵活,需要引入一个注解类,有了这个注解信息,在 around 方法中,去解析方法上的注解信息,来判断是否要打印日志,注解类定义如下:


public @interface ControllerLayerLogPrintControl {    /* 入参日志打印控制 默认为true */    boolean input() default true;    /** 出参日志打印控制 默认为true */    boolean output() default true;}
复制代码


当然,如果需要的话,还可以指定日志的打印级别。有了这个注解类,再重构一下 around 方法


 @Around("writeLog()")public Object aroundMethod(ProceedingJoinPoint joinPoint) {    MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();    ControllerLayerLogPrintControl logPrintControl = methodSignature.getMethod().getAnnotation(ControllerLayerLogPrintControl.class);    String methName= methodSignature.getDeclaringTypeName() + "." + methodSignature.getName();    Object[] args = joinPoint.getArgs();//获取参数值    String[] parameterNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();//参数名
if (logPrintControl == null || logPrintControl.input()) { //控制是否要打印入参 StringBuilder sb = new StringBuilder(); //拼接日志相关代码 log.info(sb.toString()); }
try { final Object proceed = joinPoint.proceed(); if (logPrintControl == null || logPrintControl.output()) { //控制是否要打印出参 String resultJson = JSONObject.toJSONString(proceed); log.info("方法路径: {} 返回值: {}", methName, resultJson); } return proceed; } catch (Throwable e) { e.printStackTrace(); } return Result.error();}
复制代码


实现起来特别简单,有个不好的地方就是代码看起来比较凌乱。接下来,继续重构,让它看起来更美好一点。在这里引入闭包,大体思路是将打印入参的代码和打印出参的代码封装到闭包中(其实就是一个内部类的两个方法)。


首先定义一个日志打印器接口 LogPrinter


public interface LogPrinter {    /**打印请求入参*/    void printInput();
/** * 打印请求返回值 * @param result 从controller中拿到的返回值 */ void printOutput(Object result);}
复制代码


接下来在切面类中新增一个方法:


private LogPrinter getLogPrinter(final ProceedingJoinPoint joinPoint) {    MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();    ControllerLayerLogPrintControl logPrintControl = methodSignature.getMethod().getAnnotation(ControllerLayerLogPrintControl.class);    String methName= methodSignature.getDeclaringTypeName() + "." + methodSignature.getName();    Object[] args = joinPoint.getArgs();//获取参数值    String[] parameterNames = methodSignature.getParameterNames();//参数名
return new LogPrinter() { @Override public void printInput() { if (logPrintControl == null || logPrintControl.input()) { StringBuilder sb = new StringBuilder(); //拼接日志相关代码 log.info(sb.toString()); } } @Override public void printOutput(Object result) { if (logPrintControl == null || logPrintControl.output()) { log.info("方法路径: {} 返回值: {}", methName, JSONObject.toJSONString(result)); } } };}
复制代码


最后,再改造一下 around 方法


@Around("writeLog()")public Object aroundMethod(ProceedingJoinPoint joinPoint) {    LogPrinter logPrinter = getLogPrinter(joinPoint);    logPrinter.printInput();    try {        final Object proceed = joinPoint.proceed();        logPrinter.printOutput(proceed);        return proceed;    } catch (Throwable e) {e.printStackTrace();}    return Result.error();}
复制代码


可以看出 aroundMethod方法确实简洁美观了不少。 getLogPrinter 方法接收JoinPoint作为参数,最后返回一个内部类,该内部类其实就是闭包的一种体现,闭包中维持了它所属作用域的所有变量,printInput调完后,可以直接调用 printOutput 因为方法的元数据都可以被它们共享。

发布于: 2021 年 11 月 10 日阅读数: 6
用户头像

LSJ

关注

微笑面对每一天 2018.11.11 加入

一个具有N年编程功力却早已拥有2N年工作经验的boy

评论

发布
暂无评论
以闭包方式实现一个Controller层打印日志的切面