среда, 6 августа 2025 г.

Expression resolver

 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;
    }
}

@Slf4j
public 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) {
}

Комментариев нет:

Отправить комментарий