【问题标题】:Bind byte buddy method delegation only to methods with annotated parameters仅将字节伙伴方法委托绑定到带有注释参数的方法
【发布时间】:2015-04-29 10:02:39
【问题描述】:

我想装饰现有对象,以便自动验证方法调用。我已经设法将方法调用委托给调用 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();
    }
}

【问题讨论】:

    标签: java hibernate-validator byte-buddy


    【解决方案1】:

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

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

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

    【讨论】:

      【解决方案2】:

      实际上,与其只检查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-07-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多