spring通过AOP+SPEL表达式解析实现注解式日志框架开发

概要

在我们的日日常工作当中,遇到一些比较重要的业务,需要记录一下业务的通用日志数据,包含处理前的状态信息,处理后的状态信息等一系列的数据。例如:我们需要定义一个Log对象包含(String msg,Integer operatorId,String ip,Date date)将整个数据执行前后的Context参数赋值到我们需要保存的日志当中。又因为每个日志记录的数据可能存在差异,在我们通过LogUtil.insertLog(Log log)类似的这种方法在保存数据时会出现Log.msg差异比较大的情况,没办法通过AOP去复用。

那么我们如何在Log对象当中的msg参数不一致的情况下使用AOP来实现一个通用的记录模板了,这个就是题主今天想分享的内容。

整体架构流程

传统的记录日志的操作流程,每个需要记录日志的service都需要注入LogUtil对象,对msg信息进行特殊赋值,然后存储到数据库当中。
在这里插入图片描述
使用AOP去记录日志,我们将记录日志的处理逻辑放到AOP的后置处理的代码逻辑当中即可,架构如下图,但是此时出现了一个问题,AOP后置处理时的Log对象当中的msg差异很大,怎么处理,我需要根据不同的接口设置的参数有可能是从数据库当中获取的,或者从接口的requestBody获取的我该怎么办了,接下来就该引入我们的SPEL表达式了
在这里插入图片描述

spring的SPEL表达式

SpEL(Spring Expression Language)是Spring框架中用于表达式语言的一种方式。它类似于其他编程语言中的表达式语言,用于在运行时计算值或执行特定任务。

  1. 有了它我们就可以在注解当中注册我们需要的请求参数和参数模板,在AOP代理对象执行真正的日志保存方法时,将我们需要存储的模板当中的SPEL表达式的参数替换成我们想要的参数了,解决完参数的替换。
  2. 我们还需要解决另外一个问题,我们需要在运行时获取数据库当中的数据,将数据写入到Log的msg对象当中,该如何处理了?这是我们需要封装一个对于SPEL表达式的当中的对于函数的解析和处理了。

具体的实现步骤

FBI WARNING:当时博主实现这个代码逻辑时,本地的spring版本有些低。很多的代码是抄的spring的源码实现,如果有小伙伴看大代码上注释参数springxxx的代码,本地又存在那个类,可以直接引用spring的类

  1. 首先看一下题主做出来的效果,如何在controller当中使用
    @RequestMapping(value = "save", method = {RequestMethod.POST})
    @ResponseBody
    @LogRecord(
            successContent = "订单编辑12341421,[order/save,参数]{order_new:'#order', order_old:'F(queryOrderById(#order.id))'}",
            operatorId = "#adminUserId", OperatorUserType = OperatorEnum.ADMIN,
            operatorIp = "#ip", bizType = LogTypeEnum.OperationUpdate)
    public OperationResultDto save(
            HttpServletRequest request,
            @RequestBody OrderReqDto order) throws Exception {

        OperationResultDto resultDto = orderService.updateOrder(order, uid);

        return resultDto;

    }
  1. 定义一个通用的日志记录注解
/**
 * 自定义日志注解,通过aop实现日志插入,该注解中的参数需要符合spEL解析规则,
 * 参数命名来源,方法入参和{@link LogRecordValueParser}中自定义参数
 * {@link LogRecordEvaluationContext}中自定义参数
 * 使用方式参照{@see OrderController.save} 方法注释掉的代码
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogRecord {

    /**
     * 这部分的数据就是我们需要认为去定制的数据模板,成功时会将这个数据模板的数据写入
     * 到数据库当中,大家根据自己业务当中定义的通用日志记录字段来修改这边的这一系列
     * 的字段定义,这边只代表题主需要存储的业务字段定义,应该差距不大。
     * 操作成功模版内容,spEL xxxx'spEL'xxxx格式
     */
    String successContent();

    /**
     * 操作失败模版内容
     */
    String failContent() default "";

    /**
     * 操作用户类型{@link OperatorEnum}
     */
    OperatorEnum OperatorUserType();

    /**
     * 操作人id
     */
    String operatorId() default "";

    /**
     * 操作ip
     */
    String operatorIp() default "";

    /**
     * 业务操作类型  {@link LogTypeEnum}
     */
    LogTypeEnum bizType();
}

  1. 定义参数解析器,题主判断如果参数当中有HttpServletRequest时会自动加入一个ip解析。
// 参数解析器
public class LogRecordValueParser {

    private static final DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();

    public static LogRecordEvaluationContext createEvaluationContext(Method method, Object[] arguments) {
        LogRecordEvaluationContext evaluationContext = new LogRecordEvaluationContext(method, arguments);
        String[] parameterNames = discoverer.getParameterNames(method);
        //获取方法的实际参数值
        if (parameterNames != null) {
            for (int len = 0; len < parameterNames.length; len++) {
                //当方法参数中有HttpServletRequest时,设置请求ip
                if (arguments[len] instanceof HttpServletRequest) {
                    evaluationContext.setVariable("ip", IpUtil.getIpAddr((HttpServletRequest) arguments[len]));
                }
                evaluationContext.setVariable(parameterNames[len], arguments[len]);
            }
        }
        return evaluationContext;
    }
}

//将所有解析出来的参数放入到LogRecordEvaluationContext当中,这个类是对 //StandardEvaluationContext的实现,当我们需要通过SPEL解析时,就需要将所有的参数  //对于key-value的容器就是上面的LogRecordEvaluationContext当中参数使用。大家可以 //理解它就是一个容器,SPEL在解析参数时会到这个容器当中取需要解析key对于的value。
public class LogRecordEvaluationContext extends StandardEvaluationContext {

    Logger logger = LoggerFactory.getLogger(LogRecordEvaluationContext.class);

    /**
     * 构造包含统一的基础参数的context
     */
    public LogRecordEvaluationContext(Method method, Object[] arguments) {
        //把方法的参数都放到 SpEL 解析的 RootObject 中
        super(null);
        //把用户信息设置进去,这边的话题主是将spring当中的用户上下文当中的用户id
        //解析放入进去,但是有些接口是没有权限验证的,所以不一定能获取到,所以没有
        //获取到时不影响正常的业务逻辑
        try {
            Integer uid = UserContext.getUserDetails().getUserId();
            setVariable("adminUserId", uid);
        } catch (Exception ex) {
            logger.error("获取管理端操作人信息异常!");
            setVariable("adminUserId", null);
        }

    }

    /**
     * 设置执行结果
     *
     * @param executeResult  执行结果
     * @param executeSuccess 执行是否成功
     * @param errorMsg       错误信息
     * @return LogRecordEvaluationContext
     */
    public LogRecordEvaluationContext setResult(Object executeResult, Boolean executeSuccess, String errorMsg) {
        if (executeResult != null) {
            setVariable("methodResult", executeResult);
        }
        //方法是否执行成功
        setVariable("isSuccess", executeSuccess);
        //执行失败后的错误信息
        setVariable("errorMsg", errorMsg);
        return this;
    }
}


//自定义EL表达式解析器,这个类就是使用
//上面的LogRecordEvaluationContext进行参数解析啦
public class LogRecordExpressionEvaluator extends CachedExpressionEvaluator {

    private Map<ExpressionKey, Expression> expressionCache = new ConcurrentHashMap<>(64);

    private final Map<AnnotatedElementKey, Method> targetMethodCache = new ConcurrentHashMap<>(64);

	//实际解析的地方。
    public Object parseExpression(String conditionExpression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {
        return getExpression(this.expressionCache, methodKey, conditionExpression).getValue(evalContext, Object.class);
    }
}

/**
 * @author yxl
 * @description 参考Spring4.0 CachedExpressionEvaluator,
 * 用于缓存解析出来的expression,提升效率
 * @since jdk1.8
 */
public abstract class CachedExpressionEvaluator {

    private final SpelExpressionParser parser;

    private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();


    /**
     * Create a new instance with the specified {@link SpelExpressionParser}.
     */
    protected CachedExpressionEvaluator(SpelExpressionParser parser) {
        Assert.notNull(parser, "SpelExpressionParser must not be null");
        this.parser = parser;
    }

    /**
     * Create a new instance with a default {@link SpelExpressionParser}.
     */
    protected CachedExpressionEvaluator() {
        this(new SpelExpressionParser());
    }


    /**
     * Return the {@link SpelExpressionParser} to use.
     */
    protected SpelExpressionParser getParser() {
        return this.parser;
    }

    /**
     * Return a shared parameter name discoverer which caches data internally.
     *
     * @since 4.3
     */
    protected ParameterNameDiscoverer getParameterNameDiscoverer() {
        return this.parameterNameDiscoverer;
    }


    /**
     * Return the {@link Expression} for the specified SpEL value
     * <p>Parse the expression if it hasn't been already.
     *
     * @param cache      the cache to use
     * @param elementKey the element on which the expression is defined
     * @param expression the expression to parse
     */
    protected Expression getExpression(Map<ExpressionKey, Expression> cache,
                                       AnnotatedElementKey elementKey, String expression) {

        ExpressionKey expressionKey = createKey(elementKey, expression);
        Expression expr = cache.get(expressionKey);
        if (expr == null) {
            expr = getParser().parseExpression(expression);
            cache.put(expressionKey, expr);
        }
        return expr;
    }

    private ExpressionKey createKey(AnnotatedElementKey elementKey, String expression) {
        return new ExpressionKey(elementKey, expression);
    }


    /**
     * An expression key.
     */
    protected static class ExpressionKey implements Comparable<ExpressionKey> {

        private final AnnotatedElementKey element;

        private final String expression;

        protected ExpressionKey(AnnotatedElementKey element, String expression) {
            Assert.notNull(element, "AnnotatedElementKey must not be null");
            Assert.notNull(expression, "Expression must not be null");
            this.element = element;
            this.expression = expression;
        }

        @Override
        public boolean equals(Object other) {
            if (this == other) {
                return true;
            }
            if (!(other instanceof ExpressionKey)) {
                return false;
            }
            ExpressionKey otherKey = (ExpressionKey) other;
            return (this.element.equals(otherKey.element) &&
                    ObjectUtils.nullSafeEquals(this.expression, otherKey.expression));
        }

        @Override
        public int hashCode() {
            return this.element.hashCode() * 29 + this.expression.hashCode();
        }

        @Override
        public String toString() {
            return this.element + " with expression \"" + this.expression + "\"";
        }

        @Override
        public int compareTo(ExpressionKey other) {
            int result = this.element.toString().compareTo(other.element.toString());
            if (result == 0) {
                result = this.expression.compareTo(other.expression);
            }
            return result;
        }
    }

}

/**
 * @author yxl
 * @description 参考spring4.2 AnnotatedElementKey
 * @date 2021/11/17 7:52 下午
 * @since jdk1.8
 */
public final class AnnotatedElementKey implements Comparable<AnnotatedElementKey> {

    private final AnnotatedElement element;

    private final Class<?> targetClass;

    /**
     * Create a new instance with the specified {@link AnnotatedElement} and
     * optional target {@link Class}.
     */
    public AnnotatedElementKey(AnnotatedElement element, Class<?> targetClass) {
        Assert.notNull(element, "AnnotatedElement must not be null");
        this.element = element;
        this.targetClass = targetClass;
    }


    @Override
    public boolean equals(Object other) {
        if (this == other) {
            return true;
        }
        if (!(other instanceof AnnotatedElementKey)) {
            return false;
        }
        AnnotatedElementKey otherKey = (AnnotatedElementKey) other;
        return (this.element.equals(otherKey.element) &&
                ObjectUtils.nullSafeEquals(this.targetClass, otherKey.targetClass));
    }

    @Override
    public int hashCode() {
        return this.element.hashCode() + (this.targetClass != null ? this.targetClass.hashCode() * 29 : 0);
    }

    @Override
    public String toString() {
        return this.element + (this.targetClass != null ? " on " + this.targetClass : "");
    }

    @Override
    public int compareTo(AnnotatedElementKey other) {
        int result = this.element.toString().compareTo(other.element.toString());
        if (result == 0 && this.targetClass != null) {
            if (other.targetClass == null) {
                return 1;
            }
            result = this.targetClass.getName().compareTo(other.targetClass.getName());
        }
        return result;
    }

}

  1. 参数我们都设置好了,接下来我们看一下方法应该如何取解析了。
/**
 * @author yxl
 * @description SPEL自定义解析函数,自定义SPEL表达式当中出现的解析函数,
 * 例如我最上面案例当中queryOrderById方法。
 * @since jdk1.8
 */
public abstract class IParseFunction {

    /**
     * 方法名称
     *
     * @return 自定义函数的方法名称
     */
    abstract String functionName();

    /**
     * 执行解析函数
     *
     * @param value 执行参数
     * @return 执行结果
     */
    public abstract Object execute(Object value);

    /**
     * 是否是前置执行
     *
     * @return boolean
     */
    public abstract Boolean executeBeforeAdvice();
}

/**
 * @author yxl
 * @description 查询订单的自定义函数解析的实现类。
 * @since jdk1.8
 */
@Component
public class OrderParseFunction extends IParseFunction implements InitializingBean {

    @Autowired
    private OrderDao orderDao;

    @Override
    String functionName() {
        return "queryOrderById";
    }

    @Override
    public Object execute(Object value) {
        if (!(value instanceof Integer)) {
            throw new IllegalArgumentException("自定义解析函数根据订单id获取订单信息异常");
        }
        Order order = orderDao.selectByPrimaryKey((Integer) value);
        if (order != null) {
            return order;
        } else {
            return null;
        }
    }

    @Override
    public Boolean executeBeforeAdvice() {
        return false;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        ParseFunctionFactory.registerFunction(this);
    }
}


/**
 * @author yxl
 * @description 自定义解析函数工厂类
 * @since jdk1.8
 */
public class ParseFunctionFactory {

    /**
     * 获取所有的自定义解析函数集合
     */
    private static final Map<String, IParseFunction> parseFunctionMap = new HashMap<>();

    /**
     * 根据解析函数名称获取可用的解析函数
     *
     * @param methodName 解析函数需自定义的functionName
     * @param param      需要执行解析函数的参数信息
     * @return 执行结果
     * @see IParseFunction#functionName()
     */
    public static Object executeByMethodName(String methodName, Object param) {
        IParseFunction iParseFunction = parseFunctionMap.get(methodName);
        if (iParseFunction == null) {
            throw new IllegalArgumentException("非法的自定义解析函数");
        }
        return iParseFunction.execute(param);
    }

    /**
     * 根据方法名称获取自定义解析函数
     *
     * @param methodName 方法名称
     * @return 自定义解析函数实现类
     */
    public static IParseFunction getParseFunctionByMethodName(String methodName) {
        IParseFunction iParseFunction = parseFunctionMap.get(methodName);
        if (iParseFunction == null) {
            throw new IllegalArgumentException("非法的自定义解析函数");
        }
        return iParseFunction;
    }

    /**
     * 注册所有的可用的解析函数
     *
     * @param function 解析函数
     */
    protected static void registerFunction(IParseFunction function) {
        parseFunctionMap.put(function.functionName(), function);
    }
}

//我们只解析了自定义方法,还需要解析自定义方法当中的参数。
//order_old:'F(queryOrderById(#order.id))'当中的#order.id参数

/**
 * @author yxl
 * @description 自定义方法参数名称解析类,参考spring4.0 DefaultParameterNameDiscoverer
 * @since jdk1.8
 */
public class DefaultParameterNameDiscoverer extends PrioritizedParameterNameDiscoverer {

    public DefaultParameterNameDiscoverer() {
        // 这里非常非常需要注意的一点是:用于存储的是一个LinkedList(见父类:PrioritizedParameterNameDiscoverer)
        // LinkedList是先进先出的。所以for循环遍历的时候,会最先执行Kotlin、Standard、Local... 按照这个优先级
        addDiscoverer(new StandardReflectionParameterNameDiscoverer());
        addDiscoverer(new LocalVariableTableParameterNameDiscoverer());
    }


    //自定义获取方法参数名称,参考spring4.0 StandardReflectionParameterNameDiscoverer
    static class StandardReflectionParameterNameDiscoverer implements ParameterNameDiscoverer {
        public String[] getParameterNames(Method method) {
            return getParameterNames(method.getParameters());
        }

        public String[] getParameterNames(Constructor<?> ctor) {
            return getParameterNames(ctor.getParameters());
        }

        // 使用 JDK 自带的 Parameter 解析参数名称
        private String[] getParameterNames(Parameter[] parameters) {
            String[] parameterNames = new String[parameters.length];
            for (int i = 0; i < parameters.length; i++) {
                Parameter param = parameters[i];
                if (!param.isNamePresent()) {
                    return null;
                }
                parameterNames[i] = param.getName();
            }
            return parameterNames;
        }
    }
}

  1. 通过spring的AOP去代理我们需要做日志记录的方法,通过SPEL表达式的解析规则去解析我们的自定义参数和自定义函数。



/**
 * @author yxl
 * @description 过滤方法包含LogRecord注解的类
 * @since jdk1.8
 */
public class LogRecordPointcut extends StaticMethodMatcherPointcut {
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        LogRecord annotation = method.getAnnotation(LogRecord.class);
        return annotation != null;
    }

    @Override
    public void setClassFilter(ClassFilter classFilter) {
        super.setClassFilter(ClassFilter.TRUE);
    }
}



/**
 * @author yxl
 * @since jdk1.8
 */
public class LogRecordPointcutAdvisor extends AbstractBeanFactoryPointcutAdvisor {

    LogRecordPointcut pointcut = new LogRecordPointcut();

    //配置是否动态扫描,默认静态
    public void setClassFilter(ClassFilter classFilter) {
        this.pointcut.setClassFilter(classFilter);
    }

    @Override
    public Pointcut getPointcut() {
        return pointcut;
    }

}


/**
 * @author yxl
 * @description 自定义aop切入方法执行结果
 * @since jdk1.8
 */
@Data
@AllArgsConstructor
public class MethodExecuteResult {

    /**
     * 执行结果
     */
    private Boolean executeResult;

    /**
     * 执行异常
     */
    private Throwable throwable;

    /**
     * 异常信息描述
     */
    private String errorMsg;
}


/**
 * @author yxl
 * @description 自定义aop advice增强
 * @since jdk1.8
 */
public class LogRecordInterceptor implements MethodInterceptor {

    /**
     * 匹配普通参数的正则表达式
     */
    Pattern pattern = Pattern.compile("'(#[^']*?)'", Pattern.MULTILINE);

    /**
     * 匹配自定义函数中参数的正则表达式
     */
    Pattern customMethodParamPattern = Pattern.compile("\\(#[^']*?\\)", Pattern.MULTILINE);

    /**
     * 匹配自定义函数查询方法名称的正则表达式
     * 自定义函数格式为'F(methodName(#param))'
     */
    Pattern customMethodPattern = Pattern.compile("'F\\([^']*?\\([^']*?\\)\\)'", Pattern.MULTILINE);


    private final LogRecordExpressionEvaluator logRecordExpressionEvaluator = new LogRecordExpressionEvaluator();

    //自定义需要存储的日志service,需要时替换成自己的service
    private LogService logService;

    private final Logger log = LoggerFactory.getLogger(LogRecordInterceptor.class);

    @Autowired
    public LogRecordInterceptor(LogService logService) {
        this.logService = logService;
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Method method = invocation.getThis().getClass().getDeclaredMethod(invocation.getMethod().getName(), invocation.getMethod().getParameterTypes());
        Class<?> targetClass = invocation.getThis().getClass();
        LogRecord annotation = method.getAnnotation(LogRecord.class);
        if (annotation == null) {
            return invocation.proceed();
        }
        Object ret = null;
        MethodExecuteResult methodExecuteResult = new MethodExecuteResult(true, null, "");
        Map<String, Object> functionNameAndReturnMap = new HashMap<>();
        AnnotatedElementKey methodKey = null;
        LogRecordEvaluationContext evaluationContext = null;
        //自定义函数前置处理
        try {
            methodKey = new AnnotatedElementKey(method, targetClass);
            evaluationContext = LogRecordValueParser.createEvaluationContext(method, invocation.getArguments());
            functionNameAndReturnMap.putAll(beforeAdvice(methodKey, evaluationContext, annotation));
        } catch (Exception e) {
            log.error("前置自定义解析函数解析异常!", e);
        }
        try {
            ret = invocation.proceed();
        } catch (Exception e) {
            log.error("log record execute", e);
            methodExecuteResult = new MethodExecuteResult(false, e, e.getMessage());
        }
        //自定义函数后置处理
        try {
            functionNameAndReturnMap.putAll(afterAdvice(methodKey, evaluationContext, annotation));
        } catch (Exception e) {
            log.error("后置自定义解析函数解析异常!", e);
        }
        //日志处理
        try {
            if (evaluationContext == null) {
                log.error("evaluationContext为空!");
            } else {
                recordExecute(annotation, evaluationContext, methodKey, ret, methodExecuteResult.getExecuteResult(),
                        methodExecuteResult.getErrorMsg(), functionNameAndReturnMap);
            }
        } catch (Exception t) {
            //记录日志错误不要影响业务
            log.error("后置记录日志处理异常!", t);
        }
        if (methodExecuteResult.getThrowable() != null) {
            throw methodExecuteResult.getThrowable();
        }
        return ret;
    }

    /**
     * 代理函数执行后的日志记录处理
     *
     * @param annotation               需要执行的记录日志的注解
     * @param evaluationContext        自定义evaluationContext
     * @param methodKey                自定义methodKey
     * @param executeResult            aop切入方法执行结果结果
     * @param executeSuccess           aop切入方法是否执行成功
     * @param errorMsg                 异常信息
     * @param functionNameAndReturnMap 自定义函数解析的结果Map<解析方法SPEL,执行结果>
     */
    private void recordExecute(LogRecord annotation, LogRecordEvaluationContext evaluationContext, AnnotatedElementKey methodKey, Object executeResult, Boolean executeSuccess, String errorMsg, Map<String, Object> functionNameAndReturnMap) {
        evaluationContext.setResult(executeResult, executeSuccess, errorMsg);
        //获取需要解析的数据
        String msgSpEL = annotation.successContent();
        String msgResult = msgSpEL;
        Matcher matcher = pattern.matcher(msgSpEL);
        //替换所有的自定义解析函数执行的结果
        if (!CollectionUtils.isEmpty(functionNameAndReturnMap)) {
            for (Map.Entry<String, Object> stringStringEntry : functionNameAndReturnMap.entrySet()) {
                msgResult = msgSpEL.replace(stringStringEntry.getKey(), stringStringEntry.getValue().toString());
            }
        }
        ///todo 需要获取执行前后对象对比
        while (matcher.find()) {
            String originSpEl = matcher.group();
            String spEL = originSpEl.replace("'", "");
            Object msgParser = logRecordExpressionEvaluator.parseExpression(spEL, methodKey, evaluationContext);
            msgResult = msgResult.replaceAll(originSpEl, msgParser.toString());
            log.error("log SPELTemplateParser:{}", msgResult);
        }

        //获取操作ip
        String ipSpEL = annotation.operatorIp();
        Object ip = null;
        if (StringUtils.isNotBlank(ipSpEL)) {
            ip = logRecordExpressionEvaluator.parseExpression(ipSpEL, methodKey, evaluationContext);
        }
        //获取操作人id
        String operatorIdSpEL = annotation.operatorId();
        Object operatorId = null;
        if (StringUtils.isNotBlank(operatorIdSpEL)) {
            operatorId = logRecordExpressionEvaluator.parseExpression(operatorIdSpEL, methodKey, evaluationContext);
        }


        Log log = new Log();
        if (operatorId != null) {
            log.setUid(Integer.parseInt(operatorId.toString()));
        }
        if (ip != null) {
            log.setIpAddress(ip.toString());
        }
        log.setType(annotation.bizType().toCode());
        log.setContent(msgResult);
        log.setCreateTime(new Date());
        logService.addOperationLog(log);
    }

    /**
     * 代理函数执行前,执行前置的自定义函数解析
     *
     * @param evaluationContext   LogRecordEvaluationContext
     * @param annotation          自定义的日志注解    {{@link LogRecord}}
     * @param methodKey           AnnotatedElementKey
     * @param executeBeforeAdvice 是否是前置执行
     * @return Map<解析函数的SPEL, 解析函数的执行结果>
     */
    private Map<String, Object> processBeforeExecuteFunctionTemplate(LogRecordEvaluationContext evaluationContext, LogRecord annotation, AnnotatedElementKey methodKey, Boolean executeBeforeAdvice) {
        HashMap<String, Object> functionSPELAndExecuteResultMap = new HashMap<>();
        String msgSpEL = annotation.successContent();
        Matcher matcher = customMethodPattern.matcher(msgSpEL);
        while (matcher.find()) {
            String originSpEl = matcher.group();
            //获取自定义解析函数的函数名称
            String[] split = originSpEl.split("\\(");
            String functionMethod = split[1];
            //获取自定义解析函数的参数
            Matcher paramMatcher = customMethodParamPattern.matcher(originSpEl);
            Object param = null;
            //自定义函数暂时只支持单参数函数
            while (paramMatcher.find()) {
                String paramSPEL = paramMatcher.group();
                param = logRecordExpressionEvaluator.parseExpression(paramSPEL, methodKey, evaluationContext);
            }
            Object methodExecuteResult = null;
            IParseFunction parseFunction = ParseFunctionFactory.getParseFunctionByMethodName(functionMethod);
            if (executeBeforeAdvice.equals(parseFunction.executeBeforeAdvice())) {
                methodExecuteResult = ParseFunctionFactory.executeByMethodName(functionMethod, param);
            }
            functionSPELAndExecuteResultMap.put(originSpEl, methodExecuteResult);
        }
        return functionSPELAndExecuteResultMap;
    }


    /**
     * 前置自定义函数解析处理
     *
     * @param methodKey         自定义methodKey
     * @param evaluationContext 自定义evaluationContext{@link LogRecordEvaluationContext}
     * @param annotation        需要解析的注解信息
     * @return 自定义解析函数的执行结果
     */
    private Map<String, Object> beforeAdvice(AnnotatedElementKey methodKey, LogRecordEvaluationContext evaluationContext,
                                             LogRecord annotation) {
        Map<String, Object> functionNameAndReturnMap = new HashMap<>();
        try {
            //业务逻辑执行前的自定义函数解析
            functionNameAndReturnMap = processBeforeExecuteFunctionTemplate(evaluationContext, annotation, methodKey, true);
        } catch (Exception e) {
            log.error("自定义前置处理函数解析异常!", e);
        }
        return functionNameAndReturnMap;
    }


    /**
     * 后置自定义函数解析处理
     *
     * @param methodKey         自定义methodKey
     * @param evaluationContext 自定义evaluationContext{@link LogRecordEvaluationContext}
     * @param annotation        需要解析的注解信息
     * @return 自定义解析函数的执行结果
     */
    private Map<String, Object> afterAdvice(AnnotatedElementKey methodKey, LogRecordEvaluationContext evaluationContext,
                                            LogRecord annotation) {
        Map<String, Object> functionNameAndReturnMap = new HashMap<>();
        try {
            //业务逻辑执行前的自定义函数解析
            functionNameAndReturnMap = processBeforeExecuteFunctionTemplate(evaluationContext, annotation, methodKey, false);
        } catch (Exception e) {
            log.error("自定义后置处理函数解析异常!", e);
        }
        return functionNameAndReturnMap;
    }

}

小结

FBI WARNING:题主只是实现了这个逻辑,只做了简单测试,大家用于生产的话需要自行测试一下,当时机缘巧合看了美团的文章,看见别人的思路,按照思路去实现的。文章参考地址

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值