转换对象 MethodSignature 时出错。 Spring AOP

Error casting object MethodSignature. Spring AOP

在此先感谢您的支持。 目前我陷入了下一个问题。我开发了一个方面 class 来验证我从 RestController 的 pkg 中输入的 JSON。 符合某些特征。 我的控制器的每个方法 return 都是一个不同的 DTO 对象。 当我的逻辑没有实现时,我从我的角度创建了一个新的通用对象 return 它。当我进行测试时,我收到 CannotCastClass "xxxxDTO" 到 newErrorResponseDTO 的错误。 目前我已经可以获得方法签名或对象类型。我的想法是将 return 类型(从 methodSignature)转换为我的新 DTOResponse。对象的反应总是不同的。 我提到整个项目的架构和设计已经开发完成。我只做了方面 目前,我还没有成功。 我附上证据。谢谢

我尝试了 ResponseAdvice 和多种投射对象的方法。 我更喜欢留在方面。我得到的解决方案是将控制器中的所有响应 DTO 更改为通用对象。假设这样做是不好的做法,我更喜欢真正的解决方案

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.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Arrays;

// Other imports missing...

@Aspect
@Component("validateParameterAspect")
public class ValidatorParameterAspect {

  public static final Logger logger = Logger.getLogger(ValidatorParameterAspect.class);

  @Autowired
  ServiciosRest servicio;

  @Pointcut("execution(* com.actinver.rest.*.* (..))")
  public void executeController() {}

  @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
  public void logRequestMapping() {}

  @Around("logRequestMapping() && executeController() && args(..,@RequestBody requestBody) ")
  public Object logRequestBody(ProceedingJoinPoint joinPoint, Object requestBody) throws Throwable {
    String vlDataDecrypt = "";

    try {
      // output = joinPoint.proceed();

      System.out.println("--------------123------------");
      logger.warn("Entering in Method :  " + joinPoint.getSignature().getName());
      logger.warn("Class Name :  " + joinPoint.getSignature().getDeclaringTypeName());
      logger.warn("Arguments :  " + Arrays.toString(joinPoint.getArgs()));
      logger.warn("Target class : " + joinPoint.getTarget().getClass().getName());

      SimpleJSONDataContainer args = (SimpleJSONDataContainer) joinPoint.getArgs()[0];

      MethodSignature sign = (MethodSignature) joinPoint.getSignature();
      Class<?> ret = sign.getReturnType();
      String returnString = sign.getReturnType().getName();

      logger.warn("Signature : " + ret);

      vlDataDecrypt = AESHelper.decrypt(servicio.getSeedWord(), args.getData());

      logger.info(" Decrypt -> " + vlDataDecrypt);
      logger.info("args " + args.getData());

      ErrorDataResponse res = validDataEmpty(args.getData());

      if (res.getResult() == "2") {
        return res; // or cast Class<?>

        //return ret.cast(res);
      }

    } catch (Exception e) {
      logger.error("Stack trace -> ", e);
    }
    return joinPoint.proceed();
  }

  public ErrorDataResponse validDataEmpty(String vlDataDecrypt) {
    ErrorDataResponse errorDto = new ErrorDataResponse();

    if (vlDataDecrypt == null || vlDataDecrypt.hashCode() == "77631826690E45839D7B49B932CBC81B".hashCode()
      && vlDataDecrypt.equalsIgnoreCase("77631826690E45839D7B49B932CBC81B")) {
      errorDto.setResult("2");
      errorDto.setMensaje(RestValidatorUtil.EnumErrors.ERROR_INPUT.getMsg());
      logger.info("JSON null" + errorDto.getResult());
      return errorDto;

    }
    return errorDto;
  }
}
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

// Other imports missing...

@RestController
@RequestMapping("inicio")
public class Bursanet {
  public final static Logger logger = Logger.getLogger(Bursanet.class);

  @RequestMapping(
    value = "cashByDate",
    method = { RequestMethod.GET, RequestMethod.POST },
    consumes = "application/json",
    produces = "application/json"
  )
  public CashByDateDTO cashByDate(
    @RequestBody SimpleJSONDataContainer simpleJSONDataContainer,
    Authentication authentication
  ) {
    String vlDataDecrypt = "";
    CashByDateDTO outJson = new CashByDateDTO();
    CashByDateRequest request = null;
    try {
      UsernamePasswordAuthenticationToken userPasswordAuthenticationToken =
        (UsernamePasswordAuthenticationToken)
          ((OAuth2Authentication) authentication).getUserAuthentication();
      //////example
      return outJson;
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
}

很难分析您的代码,因为您没有提供 MCVE:

  • 您的 classes 中没有包名称。
  • 也没有导入。
  • 您使用了多个特定于项目的 classes(不是 Spring 框架的一部分),您也不在此处共享其代码。
  • 也没有Spring配置。

所以我必须在这里做一些有根据的猜测。据我所见,我可以告诉你:

  • 如果您希望 ValidatorParameterAspect.logRequestBody(..) 拦截 Bursanet.cashByDate(..) 的执行,它应该不会工作,因为

    • args(.., @RequestBody requestBody) 中,您期望该参数是目标方法签名中的最后一个参数,但实际上在 Bursanet.cashByDate(..) 中它是第一个。所以切入点永远不匹配。
    • 同样,在 args(.., @RequestBody requestBody) 中,您应该使用完全限定的 class 名称,即 args(.., @org.springframework.web.bind.annotation.RequestBody requestBody).
  • 另请注意,execution(* com.actinver.rest.*.* (..)) 仅匹配直接位于 com.actinver.rest 包中的 classes 中的方法,而不匹配任何子包中的方法。如果你也想包括那些,你需要将切入点更改为 execution(* com.actinver.rest..* (..)).

  • 在您的问题中,您提到您只想拦截 REST 控制器,但您没有将切入点匹配限制为带有 @RestController 注释的 classes。你可以通过 @within(org.springframework.web.bind.annotation.RestController) 做到这一点。现在,您是通过仅依赖 @annotation(org.springframework.web.bind.annotation.RequestMapping) 的方法来间接执行此操作,只要这些方法仅出现在 @RequestController classes 中,它也将起作用。可能你的申请就是这种情况,我只是作为细节提一下。

  • 而不是 SimpleJSONDataContainer args = (SimpleJSONDataContainer) joinPoint.getArgs()[0];,为什么不通过 args() 将第一个参数绑定到 SimpleJSONDataContainer 参数,然后只使用当前未使用的 requestBody 代码中的建议方法参数?像这样:

  @Around("logRequestMapping() && executeController() && args(@org.springframework.web.bind.annotation.RequestBody requestBody, ..)")
  public Object logRequestBody(ProceedingJoinPoint joinPoint, SimpleJSONDataContainer requestBody) throws Throwable {
    // (...)

      vlDataDecrypt = AESHelper.decrypt(servicio.getSeedWord(), requestBody.getData());

      logger.info(" Decrypt -> " + vlDataDecrypt);
      logger.info("args " + requestBody.getData());

      ErrorDataResponse res = validDataEmpty(requestBody.getData());

    // (...)
  }
  • 你定义了MethodSignature sign = (MethodSignature) joinPoint.getSignature();,但不要多次使用它,你也重复调用joinPoint.getSignature()。相反,您可以像这样重新组织代码:
      MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

      System.out.println("--------------123------------");
      logger.warn("Entering in Method :  " + methodSignature.getName());
      logger.warn("Class Name :  " + methodSignature.getDeclaringTypeName());
      logger.warn("Arguments :  " + Arrays.toString(joinPoint.getArgs()));
      logger.warn("Target class : " + joinPoint.getTarget().getClass().getName());

      Class<?> ret = methodSignature.getReturnType();
      String returnString = methodSignature.getReturnType().getName();
  • 我一直不明白为什么那么多人调用许多 JoinPoint 方法来提取日志记录的详细信息,如果相反,他们可以简单地记录连接点实例。这将显示切入点的类型(例如 execution())以及目标方法签名。好吧,如果你想列出所有的方法参数,你可以另外做这个,但是这个怎么样,这还不够吗?
      logger.warn(joinPoint);
//      logger.warn("Entering in Method :  " + methodSignature.getName());
//      logger.warn("Class Name :  " + methodSignature.getDeclaringTypeName());
      logger.warn("Arguments :  " + Arrays.toString(joinPoint.getArgs()));
//      logger.warn("Target class : " + joinPoint.getTarget().getClass().getName());
  • 这整个代码块我想你也可以删除。它甚至打印错误信息并调用 return 类型 "signature":
      Class<?> ret = methodSignature.getReturnType();
      String returnString = methodSignature.getReturnType().getName();
      logger.warn("Signature : " + ret);

现在开始可能是您的问题的部分:

      ErrorDataResponse res = validDataEmpty(requestBody.getData());
      if (res.getResult() == "2") {
        return res; // or cast Class<?>
        //return ret.cast(res);
      }

在这里,您正在使方面建议跳过 joinPoint.proceed() 调用并 return 另一个对象。您拦截的方法具有签名 public CashByDateDTO cashByDate(..),即它 return 是一个特定的 DTO 类型。如果你想 return 一个 ErrorDataResponse,这只有在 ErrorDataResponseCashByDateDTO 的子类型时才有效,但可能不是。从 class 名称我什至会说 *Response*DTO 是完全不同的对象类型。您的建议不能只是更改或忽略方法签名。无论如何,您都必须 return 一个 CashByDateDTO 对象。如果你不能在这里做到这一点,也许你正在拦截错误的方法或试图在你的方面做错误的事情。

很抱歉回复冗长,但您的代码中有太多混乱,我不得不指出一些细节。