当前位置: 首页 > 知识库问答 >
问题:

在自定义批注方面传递方法参数

万俟浩
2023-03-14

我尝试使用类似于< code > org . spring framework . cache . annotation . cache able 的东西:

自定义注释:

@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)看不出< code>key = "#id"用法有什么特别之处,相比之下,< code>Cacheable的类似用法用不同于纯文本的颜色显示。我提到IDE部分只是作为一个提示,以防它有所帮助,看起来IDE预先知道这些注释,或者它只是意识到一些在我的示例中不存在的连接。

检查实体.key中的值为“#id”,而不是预期的数字。我尝试使用表达式解析器,但可能不是以正确的方式。

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

知道吗?

共有3个答案

郑承恩
2023-03-14

我认为您可能误解了框架应该为您做什么,而不是您必须做什么。

SpEL支持无法自动触发,因此您可以访问实际(已解析)值而不是表达式本身。为什么?因为有上下文,作为开发人员,您必须提供此上下文。

Intellij中的支持也是一样的。目前,Jetbrains开发人员跟踪使用SpEL的地方,并为它们标记SpEL支持。我们没有任何方法来处理这个值是一个实际的SpEL表达式的事实(毕竟这是注释类型上的一个原始的< code>java.lang.String)。

从 4.2 开始,我们已经提取了缓存抽象在内部使用的一些实用程序。您可能希望从这些内容中受益(通常是缓存表达式计算器基于方法的评估上下文)。

新的@EventListener正在使用这些东西,因此您可以查看更多代码作为您尝试执行的事情的示例:EventExpressionEvalue ator

总之,您的定制拦截器需要基于#id值执行一些操作。这个代码片段就是这种处理的一个例子,它根本不依赖于缓存抽象。

洪景铄
2023-03-14

多亏了@StéphaneNicoll我设法创建了一个工作解决方案的第一个版本:

方面

@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;
  }
}
龙浩博
2023-03-14

添加另一种使用Spring表达式的更简单方法。请参阅下文:

您的注释:

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

您的服务:

@Service
@Transactional
public class EntityServiceImpl implements EntityService {

    @CheckEntity(keyPath = "[0]")
    public Entity getEntity(Long id) {
        return new Entity(id);
    }

    @CheckEntity(keyPath = "[1].otherId")
    public Entity methodWithMoreThanOneArguments(String message, CustomClassForExample object) {
        return new Entity(object.otherId);
    }
}  

class CustomClassForExample {
   Long otherId;
}

你的外貌:

@Component
@Aspect
public class CheckEntityAspect {

    @Before("execution(* *.*(..)) && @annotation(checkEntity)")
    public void checkEntity(JoinPoint joinPoint, CheckEntitty checkEntity) {
        Object[] args = joinPoint.getArgs();
        ExpressionParser elParser = new SpelExpressionParser();
        Expression expression = elParser.parseExpression(checkEntity.keyPath());
        Long id = (Long) expression.getValue(args);

        // Do whatever you want to do with this id 

        // This works for both the service methods provided above and can be re-used for any number of similar methods  

    }
}

PS:我添加这个解决方案,因为我觉得与其他答案相比,这是一个更简单/更清晰的方法,这可能对某些人有帮助。

 类似资料:
  • 下面是如何配置应用程序的 问题是在应用程序启动期间,我得到以下错误 并且有很长的堆栈错误堆栈和描述 null 我刚试过用两个自定义方法param注入,那也不起作用

  • 本文向大家介绍Androd自定义对话框Dialog视图及参数传递的实现方法,包括了Androd自定义对话框Dialog视图及参数传递的实现方法的使用技巧和注意事项,需要的朋友参考一下 今天给大家讲讲有关自定义对话框的相关内容,前面两篇都在在利用系统提供的函数来实现对话框,但局限性太大,当我们想自己定义视图的时候,就不能利用系统函数了,就需要我们这里的自定义对话框了,有关自定义对话框的东东,以前有写

  • 我正在编写一个库/sdk,它可以拦截任何使用自定义注释进行注释的方法。代码的工作方式有点像这样 截取这个的方面有一个切入点表达式 当我在与相同的包中描述方面时,此代码工作正常。但是,如果我创建一个单独的库并定义方面,因为它无法拦截。有帮助吗? 回应@Bond的评论 Spring版本:Spring上下文-4.1.7。发布aspectj-1.6.5问题的关键是注释不会在同一个项目中使用。在编译之后,它

  • 问题内容: 如何接受自定义类型查询参数? 上面的行在启动服务器时给出错误 问题答案: 查看有关注入的可接受类型的文档。(这同样适用于所有其他注释) 是原始类型 有一个接受单个String参数的构造函数 有一个名为或的静态方法,该方法接受单个String参数(例如,参见) 已注册ParamConverterProvider JAX-RS扩展SPI的实现,该扩展返回一个ParamConverter实例

  • 本章将会讨论如何在API设计中使用自定义方法。 自定义方法指的是五个标准方法之外的API方法。他们应当仅用于标准方法不易表达的功能。一般而言,API设计者应当尽可能优先考虑使用标准方法,而不是自定义方法。标准方法相对更简单,定义完善的语义,并且开发者也更加熟悉;这使标准方法更易用,并且使用者更难犯错。使用标准方法的另一个优势是API平台会有更好的支持,如计费、错误处理、日志、监控等等。 自定义方法

  • 在我的方面方法中,我需要获取name(自定义注释的参数)的值 由用户调用的方法: