【问题标题】:Spring AOP: How to read path variable value from URI template in aspect?Spring AOP:如何从 URI 模板中读取路径变量值?
【发布时间】:2017-04-03 11:32:36
【问题描述】:

我想创建 Spring 方面,它将由自定义注释注释的方法参数设置为由 URI 模板中的 id 标识的特定类的实例。路径变量名是注解的参数。与 Spring @PathVariable 所做的非常相似。

所以控制器方法看起来像:

@RestController
@RequestMapping("/testController")
public class TestController {

    @RequestMapping(value = "/order/{orderId}/delete", method = RequestMethod.GET)
    public ResponseEntity<?> doSomething(
            @GetOrder("orderId") Order order) {

        // do something with order
    }

}

而不是经典:

@RestController
@RequestMapping("/testController")
public class TestController {

    @RequestMapping(value = "/order/{orderId}/delete", method = RequestMethod.GET)
    public ResponseEntity<?> doSomething(
            @PathVariable("orderId") Long orderId) {

        Order order = orderRepository.findById(orderId);
        // do something with order
    }
}

注解来源:

// Annotation
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface GetOrder{

    String value() default "";
}

方面来源:

// Aspect controlled by the annotation
@Aspect
@Component
public class GetOrderAspect {

    @Around( // Assume the setOrder method is called around controller method )
    public Object setOrder(ProceedingJoinPoint jp) throws Throwable{

        MethodSignature signature = (MethodSignature) jp.getSignature();
        @SuppressWarnings("rawtypes")
        Class[] types = signature.getParameterTypes();
        Method method = signature.getMethod();
        Annotation[][] annotations = method.getParameterAnnotations();
        Object[] values = jp.getArgs();

        for (int parameter = 0; parameter < types.length; parameter++) {
            Annotation[] parameterAnnotations = annotations[parameter];
            if (parameterAnnotations == null) continue;

            for (Annotation annotation: parameterAnnotations) {
                // Annotation is instance of @GetOrder
                if (annotation instanceof GetOrder) {
                    String pathVariable = (GetOrder)annotation.value();                        

                    // How to read actual path variable value from URI template?
                    // In this example case {orderId} from /testController/order/{orderId}/delete

                    HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder
                            .currentRequestAttributes()).getRequest();
                    ????? // Now what?

                }
           } // for each annotation
        } // for each parameter
        return jp.proceed();
    }
}

2017 年 4 月 4 日更新:

Mike Wojtyna 给出的答案回答了问题 -> 因此它被接受了。

OrangeDog 给出的答案使用现有 Spring 工具从不同的角度解决了问题,而不会冒新方面的实施问题的风险。如果我早知道,就不会问这个问题了。

谢谢!

【问题讨论】:

  • 您是否查看了@PathVariable 使用方式的来源?
  • 不,我没有,老实说我不知道​​从哪里开始,这很模糊。
  • 顺便说一句,您应该对/order/{orderId} 的网址使用带有删除动词的@DeleteMapping,而不是带有/order/{orderId}/delete 网址的“get”。此外,ID 通常更适合作为 uuid,而不是数字。此外,在删除之前不加载资源会浪费计算资源,因为在大多数后备存储中,您可以发出 delete by key 类型命令。
  • 哦,是的,没发现。如果您使用 GET 进行状态更改操作,您将会遇到麻烦。
  • 这只是一个例子,简化了实际问题,仅此而已。谢谢您的建议。

标签: spring spring-mvc aspectj spring-aop


【解决方案1】:

如果您已经可以访问HttpServletRequest,您可以使用HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE spring 模板来选择请求中所有属性的映射。你可以这样使用它:

request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE)

结果是一个Map 实例(不幸的是,您需要对其进行强制转换),因此您可以对其进行迭代并获取您需要的所有参数。

【讨论】:

    【解决方案2】:

    做这种事情的最简单方法是使用@ModelAttribute,它可以进入@ControllerAdvice,以便在多个控制器之间共享。

    @ModelAttribute("order")
    public Order getOrder(@PathVariable("orderId") String orderId) {
        return orderRepository.findById(orderId);
    }
    
    @DeleteMapping("/order/{orderId}")
    public ResponseEntity<?> doSomething(@ModelAttribute("order") Order order) {
        // do something with order
    }
    

    另一种方法是实现自己的支持OrderPathVariableMethodArgumentResolver,或者注册一个Converter&lt;String, Order&gt;,现有的@PathVariable系统可以使用。

    【讨论】:

    • 您的方法以不同、更优雅的视角解决问题。谢谢!
    • 如何在自定义注解中获取以上函数的pathVariable?我问这个link
    【解决方案3】:

    假设它始终是第一个带有注释的参数,也许你想这样做:

    package de.scrum_master.aspect;
    
    import java.lang.annotation.Annotation;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.stereotype.Component;
    
    import de.scrum_master.app.GetOrder;
    
    @Aspect
    @Component
    public class GetOrderAspect {
      @Around("execution(* *(@de.scrum_master.app.GetOrder (*), ..))")
      public Object setOrder(ProceedingJoinPoint thisJoinPoint) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature();
        Annotation[][] annotationMatrix = methodSignature.getMethod().getParameterAnnotations();
        for (Annotation[] annotations : annotationMatrix) {
          for (Annotation annotation : annotations) {
            if (annotation instanceof GetOrder) {
              System.out.println(thisJoinPoint);
              System.out.println("  annotation = " + annotation);
              System.out.println("  annotation value = " + ((GetOrder) annotation).value());
            }
          }
        }
        return thisJoinPoint.proceed();
      }
    }
    

    控制台日志如下所示:

    execution(ResponseEntity de.scrum_master.app.TestController.doSomething(Order))
      annotation = @de.scrum_master.app.GetOrder(value=orderId)
      annotation value = orderId
    

    如果参数注释可以出现在任意位置,您也可以使用切入点execution(* *(..)),但这不会非常有效,因为它会捕获应用程序中每个组件的所有方法执行。因此,您至少应该将其限制为具有如下请求映射的 REST 控制器和/或方法:

    @Around("execution(@org.springframework.web.bind.annotation.RequestMapping * (@org.springframework.web.bind.annotation.RestController *).*(..))")
    

    这是一个变体

    @Around(
      "execution(* (@org.springframework.web.bind.annotation.RestController *).*(..)) &&" +
      "@annotation(org.springframework.web.bind.annotation.RequestMapping)"
    )
    

    【讨论】:

    • 我不想获取@GetOrder 注释的值,而是@GetOrder 标识的路径变量的值。
    • 但是您需要注释值才能进行下一步。我为您解决了相应的 Spring AOP 问题(您的代码甚至没有在我的 IDE 中编译)。我是 AOP 专家并发现了这个问题,因为它被标记为 spring-aop,但不是 Spring 用户。所以这是我可以为你做的部分。 Spring部分你必须自己解决。也许下次你问两个不同的问题来为每个问题找一个专家。
    • 实际获取注解值解决了例子String pathVariable = (GetOrder)annotation.value();
    • 不,这个 sn-p 正是无法编译的,它必须是 ((GetOrder) annotation).value() ;-) 此外,它应该是 if (annotation instanceof GetOrder) 而不是 if (annotation instanceof GetProfile)
    • 对,这是一个复制/粘贴问题。如果误导了你,我很抱歉。
    猜你喜欢
    • 2012-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多