Search code examples
javaspringannotations

Can spring annotation access method parameters?


Consider a UrlValidator method annotation that tests if a given url is valid before calling a method.

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UrlValdator{
    String value();
}

This is working fine when routes are static and known ahead of time. For example:

@UrlValidator("http://some.known.url")
public void doSomething();

But this is not very flexible. For example, what if the route was implicit in the doSomething() method signature? Could I somehow access it form the Spring Expression Language, or some other means? For example, this doesn't work but is what I'm shooting for

@UrlValidator("#p1")
public void doSomething(String url)

or

@UrlValidator("#p1.url")
public void doSomething(Request request)

Is it possible to make annotations dynamic this way?

Related

This is the closest I've found, but the thread is old and the accepted answer is quire cumbersome/hard to follow. Is there a minimal working example/updated way to do this?


Solution

  • Short answer: Yes.

    Long answer: ElementType specifies the target of the annotation, which can be the following: ANNOTATION_TYPE, CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE, and TYPE_PARAMETER. Were are interested in PARAMETER here. Since we want from the compiler the run our code, RetentionPolicy.RUNTIME is fine for the retention type. Next we have to add @Constraint annotation, which according to the documentation:

    Marks an annotation as being a Bean Validation constraint.

    This means, Spring will pick up your parameter and validate it in runtime. The last thing we have to do is to implement the validation itself which implies creating a class which implements ConstraintValidator interface.

    Putting it all together:

    @Target(ElementType.PARAMETER)
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy = UrlValidatorImplementation.class)
    public @interface UrlValidator{
        String message() default "Invalid url";
    }
    

    Implementation of the UrlValidatorImplementation class:

    public class UrlValidatorImplementation implements ConstraintValidator<UrlValidator, String> {
        @Override
        public void initialize(UrlValidator annotation) {
            // initialization, probably not needed
        }
    
        @Override
        public boolean isValid(String url, ConstraintValidatorContext context) {
            // implementation of the url validation
        }
    }
    

    Usage of the annotation:

    public void doSomething(@UrlValidator url) { ... }