0
点赞
收藏
分享

微信扫一扫

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

自定义注解

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

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributeExceptionHandler {
String attachmentId();
}

注解使用方法

@DistributeExceptionHandler(attachmentId = "#test.id")
public void test(Test test){

}

Aspect代码

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

@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

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

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表达式计算引擎

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

//
// 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

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

//
// 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 条评论