2

I have a library that defines some annotations. This annotation can be added to my requests, I would like to glue that annotation to some custom Validator.


External lib

Annotation class

@Target(
    AnnotationTarget.FUNCTION,
    AnnotationTarget.PROPERTY_GETTER,
    AnnotationTarget.PROPERTY_SETTER,
    AnnotationTarget.FIELD
)
@Retention(AnnotationRetention.RUNTIME)
annotation class UniqueValue()

Request class

class PostReq(
    @field:UniqueValue
    val value: String
)

My app

@Component
class UniqueValueValidator() : ConstraintValidator<UniqueValue?, String?> {

    override fun initialize(constraint: UniqueValue?) {}

    override fun isValid(value: String?, context: ConstraintValidatorContext): Boolean {
        println("validation magic")
        return true
    }

}

I cannot make spring to use my UniqueLoginValidator to validate fields annotated with UniqueValue in my request. Any ideas on how to glue them together? I cannot use @Constraint(validatedBy = [UniqueValueValidator::class]) since it's not available in external lib.

Mikooos
  • 5,360
  • 5
  • 31
  • 45

3 Answers3

0

I believe you need to add @Constraint to your annotation class:

@Constraint(validatedBy = UniqueValueValidator.class)

And the rest should be fine.

m.aibin
  • 3,528
  • 4
  • 28
  • 47
0

There is no need to define validator to be a component. In the current implementation of hibernate validator that should stand behind the spring validation facility it won't provide any value.

Now, the bad news is that it seems that without extending the hibernate validator to provide a mapping logic between the annotation and custom validator there is no way to achieve what you want, assuming that the annotations are in the external library and you cannot modify it by providing a @Constraint(validatedBy) mapping.

Here are some "directions" to solutions that can work but I doubt you want to use:

  1. As I say you can change the code of the hibernate validator and provide the mapping logic by yourself. Something like a "properties" (or whatever) file that will contain a mapping of the annotation to the actual validator that you'll have to implement in your code anyway.

  2. Alter the bytecode of the annotation when its loaded by classloader in Runtime. The altered bytecode will include this @Constraint meta-annotation.

Mark Bramnik
  • 39,963
  • 4
  • 57
  • 97
  • Thank you for your insight. I was wondering how is that `javax.validation.xxx` annotations are somehow bound to some spring validators. So there must be some internal mechanism to connect dots. Any idea how to find it? Moreover classes from hibernate like `CreditCardNumber` has an empty `@Constraint(validatedBy = { })` so I wonder how the implementation is found for it. – Mikooos Jan 21 '20 at 22:13
  • I don't really understand what "spring validators" do you mean? I think all the annotations are defined inside validation api / hibernate validator. Spring just invokes them and processes the validation errors if there are any... In general java validation api exposes a javax.validation.Validator interface. Hibernate validator provides an implementation of it. Spring internally uses class org.springframework.validation.beanvalidation.SpringValidatorAdapter (see spring-context-VERSION.jar) that encapsulates the validator – Mark Bramnik Jan 22 '20 at 10:38
  • As for CreditCardNumber, there is an associated CreditCardNumberDef class which is ConstraintDef and hence its a subject to be processed by ConstraintMapping API but these are internals of hibernate validator and AFAIK are not exposed . So if you really want to dig that deep you'll have to learn the source code of hibernate validator and extend it.... – Mark Bramnik Jan 22 '20 at 11:07
0

you could create a new Annotation extending the one from the library, and annotate that one with the validation constraint.

@UniqueValue
@Constraint(validatedBy = UniqueValueValidator.class)
public @interface ValidatedUniqueValue {
}

This means that you will have to use this newly created annotation instead of the one from the library.

Ahmed Sayed
  • 1,429
  • 12
  • 12