Spring AOP @Around建议在返回ResponseEntity时触发两次

时间:2016-12-01 01:49:13

标签: spring spring-boot aop spring-aop aspect

我对我编码的Aspect有一个问题:

@Aspect
@Component
public class MyAudit {

@Pointcut("@annotation(requestMapping)")
public void controller(RequestMapping requestMapping) {
}

@Around("controller(requestMapping)")
public Object around(ProceedingJoinPoint pjp, RequestMapping requestMapping) throws Throwable {        

...

在代码的一个路径中,我正在查找授权,我需要将信息返回给用户而不执行该方法。基本上,我需要简化流程。

我有这样的代码:

    log.warn("Not permitted per policy. Returning without executing: " + authError);      
    Map<String,String> responseBody = new HashMap<>();
    responseBody.put("path",request.getContextPath());
    responseBody.put("message",authError);
    return new ResponseEntity<>(responseBody,HttpStatus.OK);

问题在于,当我返回ResponseEntity时,它会回到我的'around'方法中。不知道为什么它会再次执行。

另一个线程上有人提到我不应该将其注释为@Component,但我尝试删除它,当我这样做时,@ Around方法根本不会执行。

有谁可以指出我做错了什么?

更新:

这是完整的类,其中包含一些与业务特定项目相关的代码。

import com.ge.aviation.paasport.model.Audit;
import com.ge.aviation.paasport.repository.AuditRepository;
import com.ge.aviation.paasport.util.SecurityUtils;
import static com.google.common.base.Strings.isNullOrEmpty;
import java.lang.annotation.Annotation;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import org.apache.http.HttpResponse;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Aspect
@Component
public class MyAudit {

    @Pointcut("@annotation(requestMapping)")
    public void controller(RequestMapping requestMapping) {
    }

    @Around("controller(requestMapping)")
    public Object around(ProceedingJoinPoint pjp, RequestMapping requestMapping) throws Throwable {        

        String authError = null;
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

        String decision = SecurityUtils.evaluatePolicy(request); 
        if(!(decision.toUpperCase().equals(SecurityUtils.PERMIT))) {
           authError = decision + " : Request not permittted per policy.";
           log.warn("Not permitted per policy. Returning without executing: " + authError);      
           Map<String,String> responseBody = new HashMap<>();
           responseBody.put("path",request.getContextPath());
           responseBody.put("message",authError);
           return new ResponseEntity<>(responseBody,HttpStatus.OK);
        }
        return pjp.proceed();
  }
}

当它在return new ResponseEntity<>(responseBody,HttpStatus.OK);建议中执行@Around时,它会第二次重新进入around方法。

我在正常流程中执行的实际方法上设置一个断点,它永远不会被命中。

似乎是这个ma.invoke方法(return ma.invoke(obj, args);)在返回ResponseEntity时被点击,并且它再次启动回到around方法。

 package java.lang.reflect;

 ...
 ...

 public final class Method extends Executable {

 ...
 ...

 @CallerSensitive
  public Object invoke(Object obj, Object... args)
      throws IllegalAccessException, IllegalArgumentException,
         InvocationTargetException
  {
      if (!override) {
          if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
              Class<?> caller = Reflection.getCallerClass();
              checkAccess(caller, clazz, obj, modifiers);
          }
      }
      MethodAccessor ma = methodAccessor;             // read volatile
      if (ma == null) {
          ma = acquireMethodAccessor();
      }
      return ma.invoke(obj, args);
  }
}

1 个答案:

答案 0 :(得分:1)

感谢您的帮助。我想通了......

第一次进入我的建议方法:

pjp =
    (org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint)
    execution(List com.myCompany.controller.ApplicationController.getApplications())

第二次进入方法(在我回复401回复后):

pjp =
    (org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint)
    execution(ResponseEntity org.springframework.boot.autoconfigure.web.BasicErrorController.error(HttpServletRequest))

我为我的方面添加了这个:

@Around("@annotation(requestMapping) && execution( * com.myCompany.controller..*.*(..))")
public Object around(ProceedingJoinPoint pjp, RequestMapping requestMapping) throws Throwable {        

它只执行一次。