For example we have an anotation like @MyLog on method with some fields wich you wanna fill with expression language.
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface MyLog {
/**
* Format: SpEl expression.* Could be used root context fields {@link MyLogExpressionRootObject}<br>
* like {@code #root.result} or {@code #root.args}, {@code #root.args[0]}<br>* or by method field name {@code #fieldName}
*/
String value() default "";
}
@Slf4j
@Aspect
@RequiredArgsConstructor
public class MyLogInterceptor {
private final MyLogProcessor myLogProcessor;
@Around(value = "@annotation(myLogAnnotation)", argNames = "joinPoint,myLogAnnotation")
public Object intercept(ProceedingJoinPoint joinPoint, MyLog myLogAnnotation) throws Throwable {var result = joinPoint.proceed();
myLogProcessor.process(joinPoint, myLogAnnotation, result);
return result;
}
}
@Slf4jpublic class MyLogProcessorImpl implements MyLogProcessor {
private final StandardEvaluationContext originalEvaluationContext;
private final MyLogExpressionEvaluator expressionEvaluator;public MyLogProcessorImpl(ConfigurableListableBeanFactory beanFactory) {
this.originalEvaluationContext = new StandardEvaluationContext();
this.originalEvaluationContext.setBeanResolver(new BeanFactoryResolver(beanFactory));
this.expressionEvaluator = new MyLogExpressionEvaluator(originalEvaluationContext);}
public void process(ProceedingJoinPoint joinPoint, MyLog myLogAnnotation, @Nullable Object result) {var targetMethod = getTargetMethod(joinPoint);
var context = expressionEvaluator.createEvaluationContext(targetMethod, joinPoint.getArgs(), result);
var annotatedElementKey = new AnnotatedElementKey(targetMethod, AopProxyUtils.ultimateTargetClass(joinPoint.getTarget()));var evaluatedValue = expressionEvaluator.getValue(myLogAnnotation.value(), annotatedElementKey, context);}
private String getValue(Object object) {
return object != null ? object.toString() : null;}}
private Method getTargetMethod(final ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
var signature = (MethodSignature) joinPoint.getSignature();
if (!signature.getDeclaringType().isInterface()) {
return signature.getMethod();
}
return joinPoint.getTarget().getClass().getDeclaredMethod(signature.getName(), signature.getParameterTypes());
}import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.context.expression.CachedExpressionEvaluator;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.lang.Nullable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class MyLogExpressionEvaluator extends CachedExpressionEvaluator {
private final Map<ExpressionKey, Expression> paramCache = new ConcurrentHashMap<>(64);
private final StandardEvaluationContext originalEvaluationContext;
MyLogExpressionEvaluator(StandardEvaluationContext originalEvaluationContext) {
this.originalEvaluationContext = originalEvaluationContext;
}
public Object getValue(String conditionExpression, AnnotatedElementKey methodKey, EvaluationContext context) {
return getExpression(this.paramCache, methodKey, conditionExpression).getValue(context);
}
/**
* Create an {@link EvaluationContext}.
*
* @param method the method
* @param args the method arguments
* @param result the return value (can be {@code null})
* @return the evaluation context
*/
public EvaluationContext createEvaluationContext(Method method, Object[] args, @Nullable Object result) {
var rootObject = new MyLogExpressionRootObject(args, result);
var evaluationContext = new MethodBasedEvaluationContext(rootObject, method, args, getParameterNameDiscoverer());
applyDelegatesTo(evaluationContext);
return evaluationContext;
}
/**
* copy past method to back compatibility with spring boot 2.
* in sb 3 method can be replaced with this.originalEvaluationContext.applyDelegatesTo(evaluationContext);
*/
private void applyDelegatesTo(StandardEvaluationContext evaluationContext) {
evaluationContext.setBeanResolver(this.originalEvaluationContext.getBeanResolver());
evaluationContext.setConstructorResolvers(new ArrayList<>(this.originalEvaluationContext.getConstructorResolvers()));
evaluationContext.setMethodResolvers(new ArrayList<>(this.originalEvaluationContext.getMethodResolvers()));
evaluationContext.setPropertyAccessors(new ArrayList<>(this.originalEvaluationContext.getPropertyAccessors()));
evaluationContext.setTypeLocator(this.originalEvaluationContext.getTypeLocator());
evaluationContext.setTypeConverter(this.originalEvaluationContext.getTypeConverter());
evaluationContext.setTypeComparator(this.originalEvaluationContext.getTypeComparator());
evaluationContext.setOperatorOverloader(this.originalEvaluationContext.getOperatorOverloader());
}
}record MyLogExpressionRootObject(Object[] args, Object result) {
}