前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring Boot 自定义注解支持EL表达式(基于 MethodBasedEvaluationContext 实现)

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

作者头像
一个会写诗的程序员
发布2021-04-02 07:53:19
3.6K0
发布2021-04-02 07:53:19
举报
文章被收录于专栏:一个会写诗的程序员的博客

自定义注解

自定义 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);
                }
            }

        }
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 自定义注解
  • 注解使用方法
  • Aspect代码
  • 为注解添加Spring EL支持
    • ExpressionRootObject
      • ExpressionEvaluator
      • SPEL表达式计算引擎
      • 核心接口
        • EvaluationContext
          • StandardEvaluationContext
            • MethodBasedEvaluationContext
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档