Spring Boot 自定义注解支持EL表达式(基于 MethodBasedEvaluationContext 实现)

2021-04-02 07:53:19 浏览数 (1)

自定义注解

自定义 DistributeExceptionHandler 注解,该注解接收一个参数 attachmentId 。 该注解用在方法上,使用该注解作为切点,实现标注该注解的方法抛异常后的统一处理。

代码语言:javascript复制
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributeExceptionHandler {
    String attachmentId();
}

注解使用方法

代码语言:javascript复制
@DistributeExceptionHandler(attachmentId = "#test.id")
public void test(Test test){
    
}

Aspect代码

拦截 DistributeExceptionHandler 注解作为切点 使用 @AfterThrowing 处理异常情况

代码语言:javascript复制
@Component
@Aspect
@Slf4j
public class DistributeExceptionAspect {

    @Autowired
    private AttachmentContentClient attachmentContentClient;

    @Autowired
    private DistTaskService distTaskService;

    private ExpressionEvaluator evaluator = new ExpressionEvaluator<>();

    @Pointcut("@annotation(DistributeExceptionHandler)")
    private void exceptionHandleMethod() {

    }

    @AfterThrowing(value = "exceptionHandleMethod()", throwing = "ex")
    public void doThrowing(JoinPoint joinPoint, Throwable ex) {
        log.error("捕获异常");
        String attachmentId = getAttachmentId(joinPoint); // 获取
        // 处理异常情况下的业务
    }

    private DistributeExceptionHandler getDistributeExceptionHandler(JoinPoint joinPoint){
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        return method.getAnnotation(DistributeExceptionHandler.class);
    }

    private String getAttachmentId(JoinPoint joinPoint) {
        DistributeExceptionHandler handler = getDistributeExceptionHandler(joinPoint);
        if (joinPoint.getArgs() == null) {
            return null;
        }
        EvaluationContext evaluationContext = evaluator.createEvaluationContext(joinPoint.getTarget(), joinPoint.getTarget().getClass(), ((MethodSignature) joinPoint.getSignature()).getMethod(), joinPoint.getArgs());
        AnnotatedElementKey methodKey = new AnnotatedElementKey(((MethodSignature) joinPoint.getSignature()).getMethod(), joinPoint.getTarget().getClass());
        return evaluator.condition(handler.attachmentId(), methodKey, evaluationContext, String.class);
    }
}

为注解添加Spring EL支持

ExpressionRootObject

代码语言:javascript复制
public class ExpressionRootObject {
    private final Object object;
    private final Object[] args;

    public ExpressionRootObject(Object object, Object[] args) {
        this.object = object;
        this.args = args;
    }

    public Object getObject() {
        return object;
    }

    public Object[] getArgs() {
        return args;
    }
}

ExpressionEvaluator

代码语言:javascript复制
public class ExpressionEvaluator extends CachedExpressionEvaluator {
    private final ParameterNameDiscoverer paramNameDiscoverer = new DefaultParameterNameDiscoverer();
    private final Map conditionCache = new ConcurrentHashMap<>(64);
    private final Map targetMethodCache = new ConcurrentHashMap<>(64);


    public EvaluationContext createEvaluationContext(Object object, Class targetClass, Method method, Object[] args) {
        Method targetMethod = getTargetMethod(targetClass, method);
        ExpressionRootObject root = new ExpressionRootObject(object, args);
        return new MethodBasedEvaluationContext(root, targetMethod, args, this.paramNameDiscoverer);
    }


    public T condition(String conditionExpression, AnnotatedElementKey elementKey, EvaluationContext evalContext, Class clazz) {
        return getExpression(this.conditionCache, elementKey, conditionExpression).getValue(evalContext, clazz);
    }

    private Method getTargetMethod(Class targetClass, Method method) {
        AnnotatedElementKey methodKey = new AnnotatedElementKey(method, targetClass);
        Method targetMethod = this.targetMethodCache.get(methodKey);
        if (targetMethod == null) {
            targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
            if (targetMethod == null) {
                targetMethod = method;
            }
            this.targetMethodCache.put(methodKey, targetMethod);
        }
        return targetMethod;
    }
}

参考:stackoverflow。

SPEL表达式计算引擎

代码语言:javascript复制
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.context.expression.CachedExpressionEvaluator;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class SimpleExpressionEvaluator extends CachedExpressionEvaluator {

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

    public EvaluationContext createMethodBasedEvaluationContext(Method method, Object[] args, BeanFactory beanFactory) {
        MethodBasedEvaluationContext evaluationContext =
                new MethodBasedEvaluationContext(null, method, args, getParameterNameDiscoverer());
        if (beanFactory != null) {
            evaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
        }
        return evaluationContext;
    }

    public <T> T eval(String expression, AnnotatedElementKey elementKey, EvaluationContext evalContext) {
        return (T) getExpression(elementKey, expression).getValue(evalContext);
    }

    public Expression getExpression(AnnotatedElementKey elementKey, String expression) {
        return getExpression(this.conditionCache, elementKey, expression);
    }

}



//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.context.expression;

import java.util.Map;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

public abstract class CachedExpressionEvaluator {
    private final SpelExpressionParser parser;
    private final ParameterNameDiscoverer parameterNameDiscoverer;

    protected CachedExpressionEvaluator(SpelExpressionParser parser) {
        this.parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
        Assert.notNull(parser, "SpelExpressionParser must not be null");
        this.parser = parser;
    }

    protected CachedExpressionEvaluator() {
        this(new SpelExpressionParser());
    }

    protected SpelExpressionParser getParser() {
        return this.parser;
    }

    protected ParameterNameDiscoverer getParameterNameDiscoverer() {
        return this.parameterNameDiscoverer;
    }

    protected Expression getExpression(Map<CachedExpressionEvaluator.ExpressionKey, Expression> cache, AnnotatedElementKey elementKey, String expression) {
        CachedExpressionEvaluator.ExpressionKey expressionKey = this.createKey(elementKey, expression);
        Expression expr = (Expression)cache.get(expressionKey);
        if (expr == null) {
            expr = this.getParser().parseExpression(expression);
            cache.put(expressionKey, expr);
        }

        return expr;
    }

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

    protected static class ExpressionKey implements Comparable<CachedExpressionEvaluator.ExpressionKey> {
        private final AnnotatedElementKey element;
        private final String expression;

        protected ExpressionKey(AnnotatedElementKey element, String expression) {
            this.element = element;
            this.expression = expression;
        }

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

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

        public String toString() {
            return this.element   (this.expression != null ? " with expression ""   this.expression : """);
        }

        public int compareTo(CachedExpressionEvaluator.ExpressionKey other) {
            int result = this.element.toString().compareTo(other.element.toString());
            if (result == 0 && this.expression != null) {
                result = this.expression.compareTo(other.expression);
            }

            return result;
        }
    }
}

核心接口

EvaluationContext

代码语言:javascript复制
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.expression;

import java.util.List;

public interface EvaluationContext {
    TypedValue getRootObject();

    List<ConstructorResolver> getConstructorResolvers();

    List<MethodResolver> getMethodResolvers();

    List<PropertyAccessor> getPropertyAccessors();

    TypeLocator getTypeLocator();

    TypeConverter getTypeConverter();

    TypeComparator getTypeComparator();

    OperatorOverloader getOperatorOverloader();

    BeanResolver getBeanResolver();

    void setVariable(String var1, Object var2);

    Object lookupVariable(String var1);
}

StandardEvaluationContext

代码语言:javascript复制
package org.springframework.expression.spel.support;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.BeanResolver;
import org.springframework.expression.ConstructorResolver;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.MethodFilter;
import org.springframework.expression.MethodResolver;
import org.springframework.expression.OperatorOverloader;
import org.springframework.expression.PropertyAccessor;
import org.springframework.expression.TypeComparator;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.TypeLocator;
import org.springframework.expression.TypedValue;
import org.springframework.util.Assert;

public class StandardEvaluationContext implements EvaluationContext {
    private TypedValue rootObject;
    private List<ConstructorResolver> constructorResolvers;
    private List<MethodResolver> methodResolvers;
    private BeanResolver beanResolver;
    private ReflectiveMethodResolver reflectiveMethodResolver;
    private List<PropertyAccessor> propertyAccessors;
    private TypeLocator typeLocator;
    private TypeConverter typeConverter;
    private TypeComparator typeComparator = new StandardTypeComparator();
    private OperatorOverloader operatorOverloader = new StandardOperatorOverloader();
    private final Map<String, Object> variables = new HashMap();

    public StandardEvaluationContext() {
        this.setRootObject((Object)null);
    }

    public StandardEvaluationContext(Object rootObject) {
        this.setRootObject(rootObject);
    }

    public void setRootObject(Object rootObject, TypeDescriptor typeDescriptor) {
        this.rootObject = new TypedValue(rootObject, typeDescriptor);
    }

    public void setRootObject(Object rootObject) {
        this.rootObject = rootObject != null ? new TypedValue(rootObject) : TypedValue.NULL;
    }

    public TypedValue getRootObject() {
        return this.rootObject;
    }

    public void setPropertyAccessors(List<PropertyAccessor> propertyAccessors) {
        this.propertyAccessors = propertyAccessors;
    }

    public List<PropertyAccessor> getPropertyAccessors() {
        this.ensurePropertyAccessorsInitialized();
        return this.propertyAccessors;
    }

    public void addPropertyAccessor(PropertyAccessor accessor) {
        this.ensurePropertyAccessorsInitialized();
        this.propertyAccessors.add(this.propertyAccessors.size() - 1, accessor);
    }

    public boolean removePropertyAccessor(PropertyAccessor accessor) {
        return this.propertyAccessors.remove(accessor);
    }

    public void setConstructorResolvers(List<ConstructorResolver> constructorResolvers) {
        this.constructorResolvers = constructorResolvers;
    }

    public List<ConstructorResolver> getConstructorResolvers() {
        this.ensureConstructorResolversInitialized();
        return this.constructorResolvers;
    }

    public void addConstructorResolver(ConstructorResolver resolver) {
        this.ensureConstructorResolversInitialized();
        this.constructorResolvers.add(this.constructorResolvers.size() - 1, resolver);
    }

    public boolean removeConstructorResolver(ConstructorResolver resolver) {
        this.ensureConstructorResolversInitialized();
        return this.constructorResolvers.remove(resolver);
    }

    public void setMethodResolvers(List<MethodResolver> methodResolvers) {
        this.methodResolvers = methodResolvers;
    }

    public List<MethodResolver> getMethodResolvers() {
        this.ensureMethodResolversInitialized();
        return this.methodResolvers;
    }

    public void addMethodResolver(MethodResolver resolver) {
        this.ensureMethodResolversInitialized();
        this.methodResolvers.add(this.methodResolvers.size() - 1, resolver);
    }

    public boolean removeMethodResolver(MethodResolver methodResolver) {
        this.ensureMethodResolversInitialized();
        return this.methodResolvers.remove(methodResolver);
    }

    public void setBeanResolver(BeanResolver beanResolver) {
        this.beanResolver = beanResolver;
    }

    public BeanResolver getBeanResolver() {
        return this.beanResolver;
    }

    public void setTypeLocator(TypeLocator typeLocator) {
        Assert.notNull(typeLocator, "TypeLocator must not be null");
        this.typeLocator = typeLocator;
    }

    public TypeLocator getTypeLocator() {
        if (this.typeLocator == null) {
            this.typeLocator = new StandardTypeLocator();
        }

        return this.typeLocator;
    }

    public void setTypeConverter(TypeConverter typeConverter) {
        Assert.notNull(typeConverter, "TypeConverter must not be null");
        this.typeConverter = typeConverter;
    }

    public TypeConverter getTypeConverter() {
        if (this.typeConverter == null) {
            this.typeConverter = new StandardTypeConverter();
        }

        return this.typeConverter;
    }

    public void setTypeComparator(TypeComparator typeComparator) {
        Assert.notNull(typeComparator, "TypeComparator must not be null");
        this.typeComparator = typeComparator;
    }

    public TypeComparator getTypeComparator() {
        return this.typeComparator;
    }

    public void setOperatorOverloader(OperatorOverloader operatorOverloader) {
        Assert.notNull(operatorOverloader, "OperatorOverloader must not be null");
        this.operatorOverloader = operatorOverloader;
    }

    public OperatorOverloader getOperatorOverloader() {
        return this.operatorOverloader;
    }

    public void setVariable(String name, Object value) {
        this.variables.put(name, value);
    }

    public void setVariables(Map<String, Object> variables) {
        this.variables.putAll(variables);
    }

    public void registerFunction(String name, Method method) {
        this.variables.put(name, method);
    }

    public Object lookupVariable(String name) {
        return this.variables.get(name);
    }

    public void registerMethodFilter(Class<?> type, MethodFilter filter) throws IllegalStateException {
        this.ensureMethodResolversInitialized();
        if (this.reflectiveMethodResolver != null) {
            this.reflectiveMethodResolver.registerMethodFilter(type, filter);
        } else {
            throw new IllegalStateException("Method filter cannot be set as the reflective method resolver is not in use");
        }
    }

    private void ensurePropertyAccessorsInitialized() {
        if (this.propertyAccessors == null) {
            this.initializePropertyAccessors();
        }

    }

    private synchronized void initializePropertyAccessors() {
        if (this.propertyAccessors == null) {
            List<PropertyAccessor> defaultAccessors = new ArrayList();
            defaultAccessors.add(new ReflectivePropertyAccessor());
            this.propertyAccessors = defaultAccessors;
        }

    }

    private void ensureConstructorResolversInitialized() {
        if (this.constructorResolvers == null) {
            this.initializeConstructorResolvers();
        }

    }

    private synchronized void initializeConstructorResolvers() {
        if (this.constructorResolvers == null) {
            List<ConstructorResolver> defaultResolvers = new ArrayList();
            defaultResolvers.add(new ReflectiveConstructorResolver());
            this.constructorResolvers = defaultResolvers;
        }

    }

    private void ensureMethodResolversInitialized() {
        if (this.methodResolvers == null) {
            this.initializeMethodResolvers();
        }

    }

    private synchronized void initializeMethodResolvers() {
        if (this.methodResolvers == null) {
            List<MethodResolver> defaultResolvers = new ArrayList();
            this.reflectiveMethodResolver = new ReflectiveMethodResolver();
            defaultResolvers.add(this.reflectiveMethodResolver);
            this.methodResolvers = defaultResolvers;
        }

    }
}

MethodBasedEvaluationContext

代码语言:javascript复制
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.context.expression;

import java.lang.reflect.Method;
import java.util.Arrays;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.ObjectUtils;

public class MethodBasedEvaluationContext extends StandardEvaluationContext {
    private final Method method;
    private final Object[] arguments;
    private final ParameterNameDiscoverer parameterNameDiscoverer;
    private boolean argumentsLoaded = false;

    public MethodBasedEvaluationContext(Object rootObject, Method method, Object[] arguments, ParameterNameDiscoverer parameterNameDiscoverer) {
        super(rootObject);
        this.method = method;
        this.arguments = arguments;
        this.parameterNameDiscoverer = parameterNameDiscoverer;
    }

    public Object lookupVariable(String name) {
        Object variable = super.lookupVariable(name);
        if (variable != null) {
            return variable;
        } else {
            if (!this.argumentsLoaded) {
                this.lazyLoadArguments();
                this.argumentsLoaded = true;
                variable = super.lookupVariable(name);
            }

            return variable;
        }
    }

    protected void lazyLoadArguments() {
        if (!ObjectUtils.isEmpty(this.arguments)) {
            String[] paramNames = this.parameterNameDiscoverer.getParameterNames(this.method);
            int paramCount = paramNames != null ? paramNames.length : this.method.getParameterTypes().length;
            int argsCount = this.arguments.length;

            for(int i = 0; i < paramCount;   i) {
                Object value = null;
                if (argsCount > paramCount && i == paramCount - 1) {
                    value = Arrays.copyOfRange(this.arguments, i, argsCount);
                } else if (argsCount > i) {
                    value = this.arguments[i];
                }

                this.setVariable("a"   i, value);
                this.setVariable("p"   i, value);
                if (paramNames != null) {
                    this.setVariable(paramNames[i], value);
                }
            }

        }
    }
}

0 人点赞