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

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

问题描述

我正在尝试使用类似于 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);
    }
}    

我的 IDE (IntelliJ) 没有发现 key = "#id" 的使用有什么特别之处,而 Cacheable 的类似用法则以不同的颜色显示比纯文本.我提到 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 注释中获取参数值的唯一方法是访问参数数组,这不是我想要的,因为此注释也可用于具有多个参数的方法.

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天全站免登陆