处理@About建议中包含和不包含@RequestBody的请求 [英] Process requests with and without @RequestBody in the @Around advice

查看:22
本文介绍了处理@About建议中包含和不包含@RequestBody的请求的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这样一个基于方面的日志记录:

@Pointcut("@annotation(Loggable)")
public void loggableAnnotation() {}

@Around("loggableAnnotation()")
public Object simpleProcess(ProceedingJoinPoint joinPoint) throws Throwable {
  return this.processWithBody(joinPoint, null);
}

@Around("loggableAnnotation() && args(@org.springframework.web.bind.annotation.RequestBody body,..)")
public Object processWithBody(ProceedingJoinPoint joinPoint, Object body) throws Throwable {
  // do things
}

当我用@RequestBody执行请求时,它工作得很好,建议processWithBody()被触发。但当我执行没有@RequestBody(仅@PathVariable@RequestParam)的请求时,simpleProcess()不会被触发,相反,在processWithBody()中,我会收到路径变量值作为body参数。

为什么会发生这种情况,以及如何以不同的方式处理两种类型的请求(如果可能,在相同的建议中)?

推荐答案

您犯了三个基本错误:

  • 您正在尝试匹配args()中的参数实参批注,但在那里没有效果,这就是processWithBody(..)匹配不需要的参数并将其绑定到body的原因。应将其传输到execution()切入点。

  • 您的切入点语法是错误的,即使您将它转移到execution(),即
    execution(* *(@org.springframework.web.bind.annotation.RequestBody *, ..))如果类型(!)参数的具有@RequestBody注释,而不是参数本身。
    为此,您需要将参数本身放在括号中,如(*),即
    execution(* *(@org.springframework.web.bind.annotation.RequestBody (*), ..))

  • 您必须确保切入点是互斥的,否则多个建议将在同一连接点上匹配。准确地说,您需要区分以下情况:

    • @Loggable批注的方法,其中第一个方法参数由@RequestBody批注
    • @Loggable批注的方法@RequestBody批注的第一个方法参数
    • @Loggable批注的方法,不带任何参数

以下是纯Java+AspectJ(没有Spring或Spring AOP)的示例,但是在Spring AOP中方面语法应该相同:

注释+驱动程序应用程序:

package de.scrum_master.app;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(METHOD)
public @interface Loggable {}
package de.scrum_master.app;

import org.springframework.web.bind.annotation.RequestBody;

public class Application {
  public static void main(String[] args) {
    Application application = new Application();
    application.doNotLogMe("foo", 11);
    application.doNotLogMeEither();
    application.doNotLogMeEither("foo", 11);
    application.logMe("foo", 11);
    application.logMeToo("foo", 11);
    application.logMeToo();
  }

  public void doNotLogMe(@RequestBody String body, int number) {}
  public void doNotLogMeEither() {}
  public void doNotLogMeEither(String body, int number) {}
  @Loggable public void logMe(@RequestBody String body, int number) {}
  @Loggable public void logMeToo(String body, int number) {}
  @Loggable public void logMeToo() {}
}

方面:

如您所见,我使用了区分上述三种情况的方法,还满足了您对我称为logIt(..)的通用辅助方法的需求。在那里,您可以放置您想要使用的所有复杂的日志记录内容,而不会在建议方法中包含任何重复的代码。

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class MyAspect {
  @Pointcut("@annotation(de.scrum_master.app.Loggable)")
  public void loggableAnnotation() {}

  @Around(
    "loggableAnnotation() && " + 
    "execution(* *())"
  )
  public Object simpleProcessWithoutParameters(ProceedingJoinPoint joinPoint) throws Throwable {
    return logIt(joinPoint, null);
  }

  @Around(
    "loggableAnnotation() && " +
    "execution(* *(!@org.springframework.web.bind.annotation.RequestBody (*), ..))"
  )
  public Object simpleProcessWithParameters(ProceedingJoinPoint joinPoint) throws Throwable {
    return logIt(joinPoint, null);
  }

  @Around(
    "loggableAnnotation() && " + 
    "execution(* *(@org.springframework.web.bind.annotation.RequestBody (*), ..)) && " +
    "args(body, ..)"
  )
  public Object processWithBody(ProceedingJoinPoint joinPoint, Object body) throws Throwable {
    return logIt(joinPoint, body);
  }

  private Object logIt(ProceedingJoinPoint joinPoint, Object body) throws Throwable {
    System.out.println(joinPoint + " -> " + body);
    return joinPoint.proceed();
  }
}

控制台日志:

execution(void de.scrum_master.app.Application.logMe(String, int)) -> foo
execution(void de.scrum_master.app.Application.logMeToo(String, int)) -> null
execution(void de.scrum_master.app.Application.logMeToo()) -> null
附注:execution(* *(@MyAnn *))execution(* *(@MyAnn (*)))之间的区别很微妙,因此很棘手。遗憾的是,它没有正确地记录在它应该在的位置here。准确地说,后一种情况根本没有文档记录,可能只是在一些AspectJ发行说明中,当然也在单元测试中。但普通用户不会在那里查看。

这篇关于处理@About建议中包含和不包含@RequestBody的请求的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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