自定义批注的方面中的传递方法参数 [英] Pass method argument in Aspect of custom annotation

查看:379
本文介绍了自定义批注的方面中的传递方法参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用类似于org.springframework.cache.annotation.Cacheable的东西:

I'm trying to use something similar to org.springframework.cache.annotation.Cacheable :

自定义注释:

@Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface CheckEntity {
        String message() default "Check entity msg";
        String key() default "";
    }

方面:

@Component
@Aspect
public class CheckEntityAspect {
    @Before("execution(* *.*(..)) && @annotation(checkEntity)")
    public void checkEntity(JoinPoint joinPoint, CheckEntitty checkEntity) {
        System.out.println("running entity check: " + joinPoint.getSignature().getName());
    }
}

服务:

@Service
@Transactional
public class EntityServiceImpl implements EntityService {

    @CheckEntity(key = "#id")
    public Entity getEntity(Long id) {
        return new Entity(id);
    }
}    

Cacheable的相似用法相比,我的IDE(IntelliJ)在key = "#id"用法中看不到任何特殊之处,在该用法中,其显示的颜色与纯文本不同.我要提到IDE部分只是为了提示它,以帮助它,它看起来像IDE预先知道了这些注释,或者只是意识到了我的示例中不存在的某些连接.

My IDE (IntelliJ) doesn't see anything special with the key = "#id" usage in contrast to similar usages for Cacheable where it's shown with different color than plain text. I'm mentioning the IDE part just as a hint in case it helps, it looks like the IDE is aware in advance about these annotations or it just realizes some connection which doesn't exist in my example.

checkEntity.key中的值为"#id",而不是预期的数字. 我尝试使用ExpressionParser,但可能没有正确的方法.

The value in the checkEntity.key is '#id' instead of an expected number. I tried using ExpressionParser but possibly not in the right way.

在checkEntity批注中获取参数值的唯一方法是访问arguments数组,这不是我想要的,因为此批注也可以用于具有多个参数的方法中.

The only way to get parameter value inside the checkEntity annotation is by accessing the arguments array which is not what I want because this annotation could be used also in methods with more than one argument.

有什么主意吗?

推荐答案

感谢 @StéphaneNic​​oll我设法创建了一个可行的解决方案的第一个版本:

Thanks to @StéphaneNicoll I managed to create a first version of a working solution:

方面

@Component
@Aspect
public class CheckEntityAspect {
  protected final Log logger = LogFactory.getLog(getClass());

  private ExpressionEvaluator<Long> evaluator = new ExpressionEvaluator<>();

  @Before("execution(* *.*(..)) && @annotation(checkEntity)")
  public void checkEntity(JoinPoint joinPoint, CheckEntity checkEntity) {
    Long result = getValue(joinPoint, checkEntity.key());
    logger.info("result: " + result);
    System.out.println("running entity check: " + joinPoint.getSignature().getName());
  }

  private Long getValue(JoinPoint joinPoint, String condition) {
    return getValue(joinPoint.getTarget(), joinPoint.getArgs(),
                    joinPoint.getTarget().getClass(),
                    ((MethodSignature) joinPoint.getSignature()).getMethod(), condition);
  }

  private Long getValue(Object object, Object[] args, Class clazz, Method method, String condition) {
    if (args == null) {
      return null;
    }
    EvaluationContext evaluationContext = evaluator.createEvaluationContext(object, clazz, method, args);
    AnnotatedElementKey methodKey = new AnnotatedElementKey(method, clazz);
    return evaluator.condition(condition, methodKey, evaluationContext, Long.class);
  }
}

表达式计算器

public class ExpressionEvaluator<T> extends CachedExpressionEvaluator {

  // shared param discoverer since it caches data internally
  private final ParameterNameDiscoverer paramNameDiscoverer = new DefaultParameterNameDiscoverer();

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

  private final Map<AnnotatedElementKey, Method> targetMethodCache = new ConcurrentHashMap<>(64);

  /**
   * Create the suitable {@link EvaluationContext} for the specified event handling
   * on the specified method.
   */
  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);
  }

  /**
   * Specify if the condition defined by the specified expression matches.
   */
  public T condition(String conditionExpression, AnnotatedElementKey elementKey, EvaluationContext evalContext, Class<T> 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;
  }
}

根对象

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

这篇关于自定义批注的方面中的传递方法参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆