'Creating a constraint validator for an external, 3rd party annotation

I have an annotation from a third-party library to which I'd like to add a custom validator implementation. Let's call it @Requirement, and define it as follows:

@Target({PACKAGE, TYPE, METHOD, FIELD})
@Retention(RUNTIME)
@interface Requirement {}

Now I would like to add a constraint validator without adding my own annotation into the mix. I cannot do this with the XML-based (META-INF/validation.xml) configuration, the service loader configuration (a javax.validation.ConstraintValidator file with my validator in it), nor can I with adding custom mappings to the HibernateValidatorConfiguration using LocalValidatorFactoryBean: all of these aforementioned possibilities end up checking for @Constraint sooner or later as well.

Is there any way whatsoever to add a @Constraint-less annotation with a validator implementation?

In my specific case, I am using Java 8 and Spring Boot 2.6.1 (which itself uses Hibernate Validator 6.2.0 and javax.validation:validation-api 2.0.1), but I am (of course) welcoming of information using any Java version and Spring version.



Solution 1:[1]

i think rewrite the validator implement may be achieve your goal. Below is the draft code:

@Log4j2
public class CustomHibernateValidator extends HibernateValidator {

    @Override
    public ValidatorFactory buildValidatorFactory(ConfigurationState configurationState) {
        ValidatorFactoryImpl validatorFactory = new ValidatorFactoryImpl(configurationState);
        CustomConstraintHelper customConstraintHelper = new CustomConstraintHelper();
        try {
            Field field = validatorFactory.getClass().getDeclaredField("constraintHelper");
            field.setAccessible(true);
            field.set(validatorFactory,customConstraintHelper);
        } catch (IllegalAccessException | NoSuchFieldException e) {
            log.error("{}",e);
        }
        customConstraintHelper.modify();

        return validatorFactory;
    }
}

class CustomConstraintHelper extends ConstraintHelper {

    public CustomConstraintHelper() {
        super();
    }

    void modify() {
        Field field = null;
        try {
            field = this.getClass().getSuperclass().getDeclaredField("builtinConstraints");
            field.setAccessible(true);

            Object o = field.get(this);
            
            Map<Class<? extends Annotation>, List<ConstraintValidatorDescriptor<?>>> tmpConstraints = new HashMap<>();
            tmpConstraints.putAll((Map<Class<? extends Annotation>, List<ConstraintValidatorDescriptor<?>>>) o);
            //inject your validator
            putConstraint(tmpConstraints, Requirement.class, RequirementValidator.class);
            field.set(this, tmpConstraints);
        } catch (NoSuchFieldException | IllegalAccessException e) {

        }

    }

    private static <A extends Annotation> void putConstraint(Map<Class<? extends Annotation>, List<ConstraintValidatorDescriptor<?>>> validators,
                                                             Class<A> constraintType, Class<? extends ConstraintValidator<A, ?>> validatorType) {
        validators.put(constraintType, Collections.singletonList(ConstraintValidatorDescriptor.forClass(validatorType, constraintType)));
    }

}

and use this implement to replace default hv via write xx.xx.CustomHibernateValidator to META-INF/services/javax.validation.spi.ValidationProvider

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 smileis2333