Я хочу украсить существующие объекты, чтобы вызовы методов проверялись автоматически. Мне уже удалось делегировать вызов метода перехватчику, который вызывает валидатор 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;
}
}
Как я могу указать ElementMatcher в Byte Buddy, чтобы он привязывался только к методам с параметрами, аннотированными аннотациями, которые аннотированы с помощью @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();
}
}