복붙노트

[SPRING] 봄 AOP : 어떻게 측면에서 URI 템플릿에서 경로 변수 값을 읽을 수 있습니까?

SPRING

봄 AOP : 어떻게 측면에서 URI 템플릿에서 경로 변수 값을 읽을 수 있습니까?

사용자 정의 주석에 의해 주석 처리 된 메서드 매개 변수를 URI 템플릿의 ID로 식별되는 특정 클래스의 인스턴스로 설정하는 Spring aspect를 생성하려고합니다. 경로 변수 이름은 주석의 매개 변수입니다. 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();
    }
}

업데이트 04 / Apr / 2017 :

Mike Wojtyna가 제공 한 답변이이 질문에 답합니다 -> 따라서 받아 들여집니다.

OrangeDog가 제공 한 답변은 새로운 측면의 구현 문제를 위험에 빠뜨리지 않고 기존 Spring 도구와 다른 관점에서 문제를 해결합니다. 이전에 내가 알았 으면이 질문에 답을 얻지 못했습니다.

고맙습니다!

해결법

  1. ==============================

    1.HttpServletRequest에 이미 액세스 할 수있는 경우 HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE spring 템플릿을 사용하여 요청의 모든 속성에 대한 맵을 선택할 수 있습니다. 다음과 같이 사용할 수 있습니다.

    HttpServletRequest에 이미 액세스 할 수있는 경우 HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE spring 템플릿을 사용하여 요청의 모든 속성에 대한 맵을 선택할 수 있습니다. 다음과 같이 사용할 수 있습니다.

    request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE)
    

    결과는 Map 인스턴스입니다 (불행히도 캐스팅해야 함). 따라서이를 반복하고 필요한 모든 매개 변수를 얻을 수 있습니다.

  2. ==============================

    2.이런 종류의 일을하는 가장 쉬운 방법은 @ControllerAdvice를 여러 컨트롤러간에 공유 할 수있는 @ModelAttribute를 사용하는 것입니다.

    이런 종류의 일을하는 가장 쉬운 방법은 @ControllerAdvice를 여러 컨트롤러간에 공유 할 수있는 @ModelAttribute를 사용하는 것입니다.

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

    또 다른 방법은 Order를 지원하는 자신의 PathVariableMethodArgumentResolver를 구현하거나 기존의 @PathVariable 시스템이 사용할 수있는 Converter 를 등록하는 것입니다.

  3. ==============================

    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
    

    매개 변수 주석이 임의의 위치에 나타날 수있는 경우에는 pointcut 실행 (* * (..))을 사용할 수도 있지만 응용 프로그램의 각 구성 요소에 대한 모든 메소드 실행을 캡처하므로 매우 효율적이지는 않습니다. 그래서 적어도 다음과 같이 요청 매핑이있는 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)"
    )
    
  4. from https://stackoverflow.com/questions/43183537/spring-aop-how-to-read-path-variable-value-from-uri-template-in-aspect by cc-by-sa and MIT license