聊聊dubbo的ValidationFilter

栏目: Java · 发布时间: 5年前

内容简介:本文主要研究一下dubbo的ValidationFilterdubbo-2.7.2/dubbo-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/filter/ValidationFilter.javadubbo-2.7.2/dubbo-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/Validation.java

本文主要研究一下dubbo的ValidationFilter

ValidationFilter

dubbo-2.7.2/dubbo-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/filter/ValidationFilter.java

@Activate(group = {CONSUMER, PROVIDER}, value = VALIDATION_KEY, order = 10000)
public class ValidationFilter implements Filter {

    private Validation validation;

    /**
     * Sets the validation instance for ValidationFilter
     * @param validation Validation instance injected by dubbo framework based on "validation" attribute value.
     */
    public void setValidation(Validation validation) {
        this.validation = validation;
    }

    /**
     * Perform the validation of before invoking the actual method based on <b>validation</b> attribute value.
     * @param invoker    service
     * @param invocation invocation.
     * @return Method invocation result
     * @throws RpcException Throws RpcException if  validation failed or any other runtime exception occurred.
     */
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        if (validation != null && !invocation.getMethodName().startsWith("$")
                && ConfigUtils.isNotEmpty(invoker.getUrl().getMethodParameter(invocation.getMethodName(), VALIDATION_KEY))) {
            try {
                Validator validator = validation.getValidator(invoker.getUrl());
                if (validator != null) {
                    validator.validate(invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments());
                }
            } catch (RpcException e) {
                throw e;
            } catch (Throwable t) {
                return AsyncRpcResult.newDefaultAsyncResult(t, invocation);
            }
        }
        return invoker.invoke(invocation);
    }

}
  • ValidationFilter实现了Filter接口,其invoke方法在validation不为null且invoker的URL中的method参数中包含有validation,且方法名不是 $ 开头的,那么就会从validation获取validator,然后进行校验

Validation

dubbo-2.7.2/dubbo-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/Validation.java

@SPI("jvalidation")
public interface Validation {

    /**
     * Return the instance of {@link Validator} for a given url.
     * @param url Invocation url
     * @return Instance of {@link Validator}
     */
    @Adaptive(VALIDATION_KEY)
    Validator getValidator(URL url);

}
  • Validation接口定义了getValidator方法

AbstractValidation

dubbo-2.7.2/dubbo-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/support/AbstractValidation.java

public abstract class AbstractValidation implements Validation {

    private final ConcurrentMap<String, Validator> validators = new ConcurrentHashMap<>();

    @Override
    public Validator getValidator(URL url) {
        String key = url.toFullString();
        Validator validator = validators.get(key);
        if (validator == null) {
            validators.put(key, createValidator(url));
            validator = validators.get(key);
        }
        return validator;
    }

    protected abstract Validator createValidator(URL url);

}
  • AbstractValidation实现了Validation接口,它定义了一个ConcurrentHashMap类型的validators,并且实现了getValidator方法,同时定义了createValidator抽象方法供子类实现

JValidation

dubbo-2.7.2/dubbo-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/support/jvalidation/JValidation.java

public class JValidation extends AbstractValidation {

    /**
     * Return new instance of {@link JValidator}
     * @param url Valid URL instance
     * @return Instance of JValidator
     */
    @Override
    protected Validator createValidator(URL url) {
        return new JValidator(url);
    }

}
  • JValidation继承了AbstractValidation,它的createValidator创建的是JValidator

Validator

dubbo-2.7.2/dubbo-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/Validator.java

public interface Validator {

    void validate(String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Exception;

}
  • Validator接口定义了validate方法

JValidator

dubbo-2.7.2/dubbo-filter/dubbo-filter-validation/src/main/java/org/apache/dubbo/validation/support/jvalidation/JValidator.java

public class JValidator implements Validator {

    private static final Logger logger = LoggerFactory.getLogger(JValidator.class);

    private final Class<?> clazz;

    private final Map<String, Class> methodClassMap;

    private final javax.validation.Validator validator;

    @SuppressWarnings({"unchecked", "rawtypes"})
    public JValidator(URL url) {
        this.clazz = ReflectUtils.forName(url.getServiceInterface());
        String jvalidation = url.getParameter("jvalidation");
        ValidatorFactory factory;
        if (jvalidation != null && jvalidation.length() > 0) {
            factory = Validation.byProvider((Class) ReflectUtils.forName(jvalidation)).configure().buildValidatorFactory();
        } else {
            factory = Validation.buildDefaultValidatorFactory();
        }
        this.validator = factory.getValidator();
        this.methodClassMap = new ConcurrentHashMap<>();
    }

    //......

    @Override
    public void validate(String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Exception {
        List<Class<?>> groups = new ArrayList<>();
        Class<?> methodClass = methodClass(methodName);
        if (methodClass != null) {
            groups.add(methodClass);
        }
        Set<ConstraintViolation<?>> violations = new HashSet<>();
        Method method = clazz.getMethod(methodName, parameterTypes);
        Class<?>[] methodClasses;
        if (method.isAnnotationPresent(MethodValidated.class)){
            methodClasses = method.getAnnotation(MethodValidated.class).value();
            groups.addAll(Arrays.asList(methodClasses));
        }
        // add into default group
        groups.add(0, Default.class);
        groups.add(1, clazz);

        // convert list to array
        Class<?>[] classgroups = groups.toArray(new Class[groups.size()]);

        Object parameterBean = getMethodParameterBean(clazz, method, arguments);
        if (parameterBean != null) {
            violations.addAll(validator.validate(parameterBean, classgroups ));
        }

        for (Object arg : arguments) {
            validate(violations, arg, classgroups);
        }

        if (!violations.isEmpty()) {
            logger.error("Failed to validate service: " + clazz.getName() + ", method: " + methodName + ", cause: " + violations);
            throw new ConstraintViolationException("Failed to validate service: " + clazz.getName() + ", method: " + methodName + ", cause: " + violations, violations);
        }
    }

    //......
}
  • JValidator实现了Validator接口,其validate方法内部使用的是javax.validation.ValidatorFactory创建的javax.validation.Validator

实例

dubbo-2.7.2/dubbo-filter/dubbo-filter-validation/src/test/java/org/apache/dubbo/validation/filter/ValidationFilterTest.java

public class ValidationFilterTest {
    private Invoker<?> invoker = mock(Invoker.class);
    private Validation validation = mock(Validation.class);
    private Validator validator = mock(Validator.class);
    private RpcInvocation invocation = mock(RpcInvocation.class);

    private ValidationFilter validationFilter;

    @BeforeEach
    public void setUp() throws Exception {
        this.validationFilter = new ValidationFilter();
    }

    @Test
    public void testItWithNotExistClass() throws Exception {
        URL url = URL.valueOf("test://test:11/test?default.validation=true");

        given(validation.getValidator(url)).willThrow(new IllegalStateException("Not found class test, cause: test"));
        given(invoker.invoke(invocation)).willReturn(new AppResponse("success"));
        given(invoker.getUrl()).willReturn(url);
        given(invocation.getMethodName()).willReturn("echo1");
        given(invocation.getParameterTypes()).willReturn(new Class<?>[]{String.class});
        given(invocation.getArguments()).willReturn(new Object[]{"arg1"});

        validationFilter.setValidation(validation);
        Result result = validationFilter.invoke(invoker, invocation);

        assertThat(result.getException().getMessage(), is("Not found class test, cause: test"));

    }

    @Test
    public void testItWithExistClass() throws Exception {
        URL url = URL.valueOf("test://test:11/test?default.validation=true");

        given(validation.getValidator(url)).willReturn(validator);
        given(invoker.invoke(invocation)).willReturn(new AppResponse("success"));
        given(invoker.getUrl()).willReturn(url);
        given(invocation.getMethodName()).willReturn("echo1");
        given(invocation.getParameterTypes()).willReturn(new Class<?>[]{String.class});
        given(invocation.getArguments()).willReturn(new Object[]{"arg1"});

        validationFilter.setValidation(validation);
        Result result = validationFilter.invoke(invoker, invocation);

        assertThat(String.valueOf(result.getValue()), is("success"));
    }

    @Test
    public void testItWithoutUrlParameters() throws Exception {
        URL url = URL.valueOf("test://test:11/test");

        given(validation.getValidator(url)).willReturn(validator);
        given(invoker.invoke(invocation)).willReturn(new AppResponse("success"));
        given(invoker.getUrl()).willReturn(url);
        given(invocation.getMethodName()).willReturn("echo1");
        given(invocation.getParameterTypes()).willReturn(new Class<?>[]{String.class});
        given(invocation.getArguments()).willReturn(new Object[]{"arg1"});

        validationFilter.setValidation(validation);
        Result result = validationFilter.invoke(invoker, invocation);

        assertThat(String.valueOf(result.getValue()), is("success"));
    }

    @Test
    public void testItWhileMethodNameStartWithDollar() throws Exception {
        URL url = URL.valueOf("test://test:11/test");

        given(validation.getValidator(url)).willReturn(validator);
        given(invoker.invoke(invocation)).willReturn(new AppResponse("success"));
        given(invoker.getUrl()).willReturn(url);
        given(invocation.getMethodName()).willReturn("$echo1");
        given(invocation.getParameterTypes()).willReturn(new Class<?>[]{String.class});
        given(invocation.getArguments()).willReturn(new Object[]{"arg1"});

        validationFilter.setValidation(validation);
        Result result = validationFilter.invoke(invoker, invocation);

        assertThat(String.valueOf(result.getValue()), is("success"));

    }


    @Test
    public void testItWhileThrowoutRpcException() throws Exception {
        Assertions.assertThrows(RpcException.class, () -> {
            URL url = URL.valueOf("test://test:11/test?default.validation=true");

            given(validation.getValidator(url)).willThrow(new RpcException("rpc exception"));
            given(invoker.invoke(invocation)).willReturn(new AppResponse("success"));
            given(invoker.getUrl()).willReturn(url);
            given(invocation.getMethodName()).willReturn("echo1");
            given(invocation.getParameterTypes()).willReturn(new Class<?>[]{String.class});
            given(invocation.getArguments()).willReturn(new Object[]{"arg1"});

            validationFilter.setValidation(validation);
            validationFilter.invoke(invoker, invocation);
        });
    }
}
  • 这里分别验证了withNotExistClass、withExistClass、withoutUrlParameters、methodNameStartWithDollar、throwoutRpcException这几个场景

小结

$

doc


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Out of their Minds

Out of their Minds

Dennis Shasha、Cathy Lazere / Springer / 1998-07-02 / USD 16.00

This best-selling book is now available in an inexpensive softcover format. Imagine living during the Renaissance and being able to interview that eras greatest scientists about their inspirations, di......一起来看看 《Out of their Minds》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器