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

在@Around通知中处理有@RequestBody和无@RequestBody的请求

宗冷勋
2023-03-14

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

@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执行请求时,它工作正常,会触发adviceprocessWithBody()。但是,当我执行没有@RequestBody的请求时(仅@PathVariable和@RequestParam)不会触发simpleProcess(),而是在processWithBody()中,我接收路径变量值作为body参数。

为什么会发生这种情况?我如何以不同的方式处理两种类型的请求(如果可能,在同一个通知中)?


共有1个答案

方野
2023-03-14

你犯了三个基本的错误:

>

您的切入点语法是错误的,即使您将其转移到execution(),例如执行(**(@org.springframework.web.bind.annotation.RequestBody*,…) 将匹配参数的有一个@RequestBody注释,而不是参数本身
为了实现这一点,您需要将参数本身放入括号中,例如执行(**(@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(…) 。在那里,您可以放置所有想要使用的复杂日志记录,而不会在您的advice方法中有任何重复代码。

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

附言:执行(**(@MyAnn*))执行(**(@MyAnn (*))))之间的区别很微妙,因此很棘手。不幸的是,这里没有正确记录它应该在的地方。准确地说,后一种情况根本没有记录,只是可能在一些AeyJ发行说明中,当然也在单元测试中。但是普通用户不会看那里。

 类似资料:
  • 我读过许多类似的问题,包括:JQuery、Spring MVC@RequestBody和JSON——使其能够将JSON请求与JQuery/Ajax与Spring一起工作 要求是服务器只会接受应用程序/json类型。我使用的是Spring MVC控制器。代码通过@响应体以JSON形式发送响应。我想通过我的Spring MVC控制器中的@Request estbody获取信息。我正在使用JSP将JSO

  • 我有时区POJO如下: 我在Spring Controller中有如下updateTimeZone方法: 当我通过以下请求时: 然后,当映射到POJO时,它会自动将其他值转换为默认值,并变为: 因此,当我如下更新此POJO时 它自动覆盖TimeZone=null和小时差异=0.0, 那么有没有什么方法可以让我在@Request estbody中的TimeZoneDto只包含我在请求JSON中传递的

  • 问题内容: 有人可以在Spring 3中解释@RequestBody和@ResponseBody注释吗?他们是干什么的?任何例子都很好。 问题答案: 在文档中有一个名为16.3.3.4的完整章节,其中将请求主体与@RequestBody批注进行映射。还有一个叫做16.3.3.5的映射,它使用@ResponseBody注释映射响应主体。我建议你查阅这些部分。也相关:@RequestBodyjavad

  • 有人能解释一下Spring3中的和注释吗?它们是干什么用的?任何例子都很好。

  • 我必须实现的逻辑是记录所有的请求与主体服务到数据库。 据我所知,在Controller中,我们可以使用和。我可以在重用它们吗?

  • 我有: 和: 我不喜欢的是手动将从放入:。 对于正文为的POST请求,如何在中自动设置,以便您获得如下所示的? 基本上,我想要这样的东西: 有没有办法做到这一点? 谢谢你!:) 编辑:这是一个老问题,仍然没有答案,所以我想为这个问题添加新的视角。 我们遇到的另一个问题是验证,即基于某个字段和进行验证的特定定义的自定义约束。 如果我们从请求体中删除,那么我们如何从自定义约束中访问它呢?:)