1. 场景:
因为工作中经常需要做参数校验,在springboot项目中使用@Valid+@NotNull、@NotBlank…注解开发API接口非常丝滑,相反在开发RPC接口时却还是需要编写大量的参数判断,严重影响主业务流程的开发(公司目前用的是Dubbo2.7.2)且代码整洁度、风格都受到了挑战。基于以上原因萌生了写一个PRC接口的验证,当然新版的dubbo已经支持了调用参数校验。
2. 原理:
因为我们要在consumer调用provider的过程中实现参数校验,而这个逻辑可以做为一个逻辑层,是否联想到AOP?而dubbo提供的filter机制刚好符合我们的要求。校验逻辑可以使用目前主流的校验框架hibernate-validator(省时省力又完善)。
3. 自定义实现:
DubboServiceParameterFilter类是我定义的dubbo filer类,当然作为filter需要使用dubbo的spi机制,在
/META-INF/dubbo/com.alibaba.rpc.Filter文件中添加:
dubboServiceParameterFilter=com.xxxx.filter.DubboServiceParameterFilter
Filter代码:
package com.example.api.demo.config.filter;
import com.google.common.collect.Maps;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.executable.ExecutableValidator;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.rpc.*;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@Slf4j
@Activate(group = {Constants.PROVIDER}, order = 1)
public class DubboServiceParameterFilter implements Filter {
// 这段代码创建了一个静态的 ExecutableValidator 实例,用于执行验证操作。它通过 Validation 工具类的 buildDefaultValidatorFactory 方法
// 获取默认的验证器工厂,并从中获取验证器实例。然后,使用 forExecutables 方法为执行体(executable)创建验证器。
private static ExecutableValidator executableValidator = Validation.buildDefaultValidatorFactory().getValidator().forExecutables();
private static final String VALIDATOR_MESSAGE = "参数验证失败";
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) {
log.info("===================== param filter ========================");
Method method = getMethod(invoker, invocation);
if (Objects.nonNull(method)) {
Class inf = invoker.getInterface();
Object object = ServiceBean.getSpringContext().getBean(inf);
Object[] paramList = invocation.getArguments();
Set<ConstraintViolation<Object>> constraintViolations = executableValidator.validateParameters(object, method, paramList);
Response response = getValidationResult(constraintViolations);
if (Objects.nonNull(response.getData())) {
return new RpcResult(response);
}
}
return invoker.invoke(invocation);
}
/**
* 获取校验方法
*/
private static Method getMethod(Invoker<?> invoker, Invocation invocation) {
Method[] methods = invoker.getInterface().getDeclaredMethods();
for (Method m : methods) {
boolean needCheck = m.getName().equals(invocation.getMethodName()) && invocation.getArguments().length == m.getParameterCount();
if (needCheck) {
if (matchMethod(invocation.getParameterTypes(), m.getParameterTypes())) {
return m;
}
}
}
return null;
}
//获取匹配的方法
private static boolean matchMethod(Class[] invokerMethodParamClassList, Class[] matchMethodParamClassList) {
for (int i = 0; i < invokerMethodParamClassList.length; i++) {
if (!invokerMethodParamClassList[i].equals(matchMethodParamClassList[i])) {
return false;
}
}
return true;
}
/**
* 校验结果转换返回对象
*/
private static <T> Response getValidationResult(Set<ConstraintViolation<T>> set) {
if (set != null && !set.isEmpty()) {
Map<String, String> errorMsg = Maps.newHashMap();
for (ConstraintViolation<T> violation : set) {
errorMsg.put(violation.getPropertyPath().toString(), violation.getMessage());
}
return Response.createError(VALIDATOR_MESSAGE, errorMsg);
}
return Response.createError(VALIDATOR_MESSAGE);
}
}
标签:dubbo,return,校验,参数,invoker,invocation,import From: https://www.cnblogs.com/lgg20/p/18222334