Spring AOP: How to read path variable value from URI template in aspect?

大憨熊 提交于 2019-12-02 03:08:46

If you already have access to HttpServletRequest, you can use HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE spring template to select map of all attributes in the request. You can use it like that:

request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE)

The result is a Map instance (unfortunately you need to cast to it), so you can iterate over it and get all the parameters you need.

The easiest way to do this sort of thing is with @ModelAttribute, which can go in a @ControllerAdvice to be shared between multiple controllers.

@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
}

Another way is to implement your own PathVariableMethodArgumentResolver that supports Order, or register a Converter<String, Order>, that the existing @PathVariable system can use.

Assuming that it is always the first parameter bearing the annotation, maybe you want to do it like this:

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

The console log would look like this:

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

If parameter annotations can appear in arbitrary positions you can also use the pointcut execution(* *(..)) but this would not be very efficient because it would capture all method executions for each component in your application. So you should at least limit it to REST controlers and/or methods with request mappings like this:

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

A variant of this would be

@Around(
  "execution(* (@org.springframework.web.bind.annotation.RestController *).*(..)) &&" +
  "@annotation(org.springframework.web.bind.annotation.RequestMapping)"
)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!