如何解决从不同类型的方法参数中提取特定字段的值
我正在为 Spring Boot 应用程序中的服务方法编写一个拦截器,如下所示:
@Aspect
public class MyAspect {
public MyAspect() {
}
@pointcut("@within(org.springframework.stereotype.Service)")
public void applicationservicepointcut() {
}
@Before(value = ("applicationservicepointcut()"))
public void process(JoinPoint joinPoint) throws Exception {
...
}
}
一种这样的服务如下:
@Service
@Transactional
public class AService {
public ADTO create(ADTO aDTO) {
...
}
public ADTO update(ADTO aDTO) {
...
}
}
另一个服务可以如下:
@Service
@Transactional
public class BService {
public BDTO create(BDTO bDTO) {
...
}
public BDTO update(BDTO bDTO) {
...
}
public void doSomething(String a,int b) {
...
}
}
这里我的目标是从关联的方法参数中提取特定字段的值。为了做到这一点,我可以在方面编写一个函数,其中我可以有多个 if-else 块,如下所示:
String extractMyField(JoinPoint joinPoint) {
//extract the arguments of the associated method from joinpoint
//depending on the type of arguments,extract the value of myfield as follows
if(arg instance of ADTO) {
...
} else if (arg instance of BDTO) {
...
}
...
}
从上面的代码片段可以看出,可能有以下几种情况:
此外,我也无法更改 DTO 对象。
我想知道是否有更好的方法来做到这一点。基本上,我正在寻找一种使其易于扩展的方法。
有人可以帮忙吗?谢谢。
编辑
Map<String,Object> getmethodArguments(JoinPoint joinPoint) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Map<String,Object> argumentNameValueMap = new HashMap<>();
if (methodSignature.getParameterNames() == null) {
return argumentNameValueMap;
}
for (int i = 0; i < methodSignature.getParameterNames().length; i++) {
String argumentName = methodSignature.getParameterNames()[i];
Object argumentValue = joinPoint.getArgs()[i];
Class argumentType = joinPoint.getArgs()[i].getClass();
//check if i th parameter is json serializable
if (Objects.isNull(methodSignature.getParameterTypes()) ||
isJsonSerializable(methodSignature.getParameterTypes()[i])) {
argumentNameValueMap.put(argumentName,mapper.convertValue(argumentValue,Map.class));
}
}
return argumentNameValueMap;
}
private boolean isJsonSerializable(Class parameterType) {
return
!Objects.isNull(parameterType) &&
!parameterType.getName().endsWith("HttpServletRequest") &&
!parameterType.getName().endsWith("HttpServletResponse") &&
!parameterType.getName().endsWith("multipartfile");
}
实际上,这里的问题是我要查找的字段在 DTO 中的命名不同。例如,在 ADTO 中,该字段被命名为 id
,而在 BDTO 中,它被命名为 aId
。
解决方法
如何让您的 ADTO/BDTO 实现自定义界面
interface AspectDataProvider {
String aspectSpecificData()
}
然后在您的参数处理器中,您可以轻松做到
if (arg instanceof AspectDataProvider) {
return ((AspectDataProvider) arg).aspectSpecificData();
}
或者您可以深入挖掘并为您的实现添加访问者模式
interface AspectImplementor {
void executeAspect(String value);
}
interface AspectAware {
void accept(AspectImplementor aspect);
}
然后在您的 BDTO ADTO 中添加 AspectAware
void accept(AspectImplementor aspect) {
String property = // object-specific property extraction
aspect.executeAspect(property);
}
在你的方面,你可以写类似的
if (arg instanceof AspectAware ) {
((AspectAware ) arg).accept(aspectExecutorInstance);
}
但我不建议从一开始就从设计模式开始——一旦你确定你有一个访问者模式,它应该总是下一个改进。
,好的,那么让我提出以下建议:
我想,你不能改变 DTO 对象 我假设,有时您的方法甚至没有 DTO 参数,但是您有一个逻辑可以“找到”它们(如果它们存在)
如果你知道截获的DTO的类型是什么,你可以创建接口:
interface DTOHandler {
Class<?> getDTOClass();
Object getMyField(Object dtoObject);
}
为每个DTO实现这个接口
例如,如果您计划拦截 2 种不同类型的 DTO:
class ADTO {
Integer myField;
}
class BDTO {
String myField;
}
然后执行:
class DTOAHandler implements DTOHandler {
Class<?> getDtoClass() {return ADTO.class;}
Object getMyField(Object obj) {return ((ADTO)obj).getMyField();}
}
// and do a similar implementation same for DTOB
现在将所有这些处理程序定义为 spring bean,并将它们的列表注入方面。 Spring boot 会将它们全部注入为列表,顺序无关紧要
在方面的构造函数中创建一个 Class> -> DTOHandler 的映射。 迭代抛出列表并创建映射的键作为“getDTOClass”的调用和作为处理程序本身的值:
public class MyAspect {
private Map<Class<?>,DTOHandler> handlersRegistry;
public MyAspect(List<DTOHandler> allHandlers) {
handlersRegistry = new HashMap<>();
for(DTOHandler h : allHandlers) {
handlersRegistry.put(h.getDTOClass(),h);
}
}
}
通过此设置,extractMyField
将如下所示:
public void extractMyField(JoinPoint jp) {
Object myDTO = findDTOParameter(jp);
DTOHandler handler = this.handlersRegistry.get(myDTO.getClass());
if(handler != null) {
// there is an actual handler for this type of DTOs
Object myField = handler.getMyField(myDTO);
}
else {
// the aspect can't handle the DTO of the specified type
}
}
更新(基于 Op 的评论):
为了实际找到具有“myfield”值的参数,您使用了一个非常复杂的逻辑(在问题的编辑部分)。
相反,您可以创建可应用于参数的注释(运行时保留)。
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface @DTO {
}
然后你可以用这个注解对服务中的参数进行注解,以“帮助”你的方面了解哪些参数实际上是 DTO:
@Service
@Transactional
public class AService {
public ADTO create(@DTO ADTO aDTO) {
...
}
public ADTO update(@DTO ADTO aDTO) {
...
}
}
那么,aspect中的解析逻辑就会变成这样:
// pseudocode
foreach p in 'parameters' {
if(isAnnotatedWith(p,DTO.class)) {
// it matches
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。