Spring AOP:如何从方面的URI模板中读取路径变量值?

时间:2017-04-03 11:32:37

标签: spring spring-mvc aspectj spring-aop

我想创建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来源:

// 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();
    }
}

更新04 / Apr / 2017:

Mike Wojtyna给出的答案回答了问题 - &gt;因此被接受了。

OrangeDog给出的答案通过现有的Spring工具从不同角度解决问题,而不会冒新实施问题的风险。如果我之前知道,那么就不会问这个问题。

谢谢!

3 个答案:

答案 0 :(得分:3)

如果您已有权访问HttpServletRequest,则可以使用HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE spring模板选择请求中所有属性的地图。您可以这样使用它:

request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE)

结果是一个Map实例(不幸的是你需要转换它),所以你可以迭代它并获得你需要的所有参数。

答案 1 :(得分:1)

执行此类操作的最简单方法是使用add_shortcode('LATEST_NOT_ROHIT','latest_notification_rohit'); function latest_notification_rohit() { ob_start(); require_once("shortcode.php"); $data = ob_get_contents(); ob_end_clean(); return $data; } ,它可以在@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 } 支持PathVariableMethodArgumentResolver,或注册现有Order系统可以使用的Converter<String, Order>

答案 2 :(得分:0)

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

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)"
)