0

我想装饰现有对象,以便自动验证方法调用。我已经设法将方法调用委托给调用 Hibernate 验证器的拦截器,到目前为止它工作正常:

public class HibernateBeanValidator implements BeanValidator{

    ValidatorFactory factory = Validation.buildDefaultValidatorFactory();

    @Override
    public <T> T addMethodValidation(T object) {
        ExecutableValidator executableValidator = factory.getValidator().forExecutables();

        Class<? extends T> dynamicType = (Class<? extends T>)new ByteBuddy()
                .subclass(object.getClass())
                .method(isPublic()).intercept(MethodDelegation.to(new ValidationInterceptor(object, executableValidator)).andThen(SuperMethodCall.INSTANCE))
                .make()
                .load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
                .getLoaded();

        try {
            T validatedObject = dynamicType.newInstance();
            return  validatedObject;
        } catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    public static class ValidationInterceptor {

        private final Object validatedObject;
        private final ExecutableValidator executableValidator;

        public <T> ValidationInterceptor(T object, ExecutableValidator executableValidator) {
            this.validatedObject = object;
            this.executableValidator = executableValidator;
        }

        public void validate(@Origin Method method, @AllArguments Object[] arguments)
                throws Exception {
            Set<ConstraintViolation<Object>> constraintViolations = executableValidator.validateParameters(validatedObject, method, arguments);
            if(! constraintViolations.isEmpty()) {
                throw new ValidationException(constraintViolations);
            }
        }
    }
}

我想改进的是仅将方法调用绑定到具有至少一个使用约束注释注释的参数的方法,例如:

class Echo {
    public String repeat(@NotNull String word) { /* should bind validation here */
        return word;
    }

    public String notAnnotated(String word) { /* should not bind validation */
        return word;
    }
}

我如何在 Byte Buddy 中指定一个 ElementMatcher,以便它只绑定到带有用 @Constraint 注释的注释注释的参数的方法,例如 @NotNull(取自 javax.validation.constraints):

@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = { })
public @interface NotNull {

    String message() default "{javax.validation.constraints.NotNull.message}";

    Class<?>[] groups() default { };

    Class<? extends Payload>[] payload() default { };

    /**
     * Defines several {@link NotNull} annotations on the same element.
     *
     * @see javax.validation.constraints.NotNull
     */
    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
    @Retention(RUNTIME)
    @Documented
    @interface List {

        NotNull[] value();
    }
}
4

2 回答 2

0

您的问题可以通过实现ElementMatcher用于识别要拦截的方法的自定义来解决。目前,您正在使用预定义的isPublic()拦截器,它不考虑注释,而只考虑public方法的修饰符。由于可以链接预定义的注释,您可以构建一个合适的匹配器,如下所示:

isPublic().and(hasParameter(hasAnnotation(nameStartsWith("javax."))))

当然,您可以简单地实现自己的匹配器而不使用预定义的匹配器。

于 2015-04-30T08:53:06.363 回答
0

实际上,与其只检查命名空间外的注释,javax.validation.constraints不如使用 Bean Validation 元数据 API。约束不需要来自这个命名空间,但也可以来自 Hibernate Validator ( org.hibernate.validator.constraints) 或者是自定义约束。使用元数据 API的可能实现ElementMatcher如下所示:

公共静态类 BeanValidationMatcher 实现 ElementMatcher {

private static final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

@Override
public boolean matches(Object target) {
    // handle different descriptors and potentially use generic MethodDescription
    if ( !( target instanceof MethodDescription.ForLoadedMethod ) ) {
        return false;
    }
    MethodDescription.ForLoadedMethod methodDescription = (MethodDescription.ForLoadedMethod) target;
    Method method = methodDescription.getLoadedMethod();

    boolean isGetter = ReflectionHelper.isGetterMethod( method );

    boolean needsValidation;
    BeanDescriptor beanDescriptor = validator.getConstraintsForClass( method.getDeclaringClass() );
    if ( isGetter ) {
        needsValidation = isGetterConstrained( method, beanDescriptor );
    }
    else {
        needsValidation = isNonGetterConstrained( method, beanDescriptor );
    }

    return needsValidation;
}

private boolean isNonGetterConstrained(Method method, BeanDescriptor beanDescriptor) {
    return beanDescriptor.getConstraintsForMethod( method.getName(), method.getParameterTypes() ) != null;
}

private boolean isGetterConstrained(Method method, BeanDescriptor beanDescriptor) {
    String propertyName = ReflectionHelper.getPropertyName( method );
    PropertyDescriptor propertyDescriptor = beanDescriptor.getConstraintsForProperty( propertyName );
    return propertyDescriptor != null && propertyDescriptor.findConstraints()
            .declaredOn( ElementType.METHOD )
            .hasConstraints();
}

}

于 2015-06-08T18:55:01.610 回答