转换对象 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
,这只有在 ErrorDataResponse
是 CashByDateDTO
的子类型时才有效,但可能不是。从 class 名称我什至会说 *Response
和 *DTO
是完全不同的对象类型。您的建议不能只是更改或忽略方法签名。无论如何,您都必须 return 一个 CashByDateDTO
对象。如果你不能在这里做到这一点,也许你正在拦截错误的方法或试图在你的方面做错误的事情。
很抱歉回复冗长,但您的代码中有太多混乱,我不得不指出一些细节。
在此先感谢您的支持。 目前我陷入了下一个问题。我开发了一个方面 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
,这只有在 ErrorDataResponse
是 CashByDateDTO
的子类型时才有效,但可能不是。从 class 名称我什至会说 *Response
和 *DTO
是完全不同的对象类型。您的建议不能只是更改或忽略方法签名。无论如何,您都必须 return 一个 CashByDateDTO
对象。如果你不能在这里做到这一点,也许你正在拦截错误的方法或试图在你的方面做错误的事情。
很抱歉回复冗长,但您的代码中有太多混乱,我不得不指出一些细节。