how do i get annotated methods with a given annotation in a springboot java app?

Here is my stand-alone AspectJ MCVE. I just imported some Spring classes. The syntax would be the same in Spring AOP.

Helper classes:

package de.scrum_master.app;

public class Result {
  public Result(int i) {}
}
package de.scrum_master.app;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Retention(RUNTIME)
@Target(METHOD)
public @interface ValidateAction {}

Custom validator interface (not annotation):

package de.scrum_master.app;

public interface MyValidator {
  boolean validate();
}

Class implementing custom validator interface:

package de.scrum_master.app;

public class InputRequest implements MyValidator {
  private Integer a;
  private Integer b;

  public InputRequest(Integer a, Integer b) {
    this.a = a;
    this.b = b;
  }

  @Override
  public boolean validate() {
    System.out.println("Performing custom validation");
    return false;
  }

  public Integer getA() {
    return a;
  }

  public Integer getB() {
    return b;
  }

  @Override
  public String toString() {
    return "InputRequest(a=" + a + ", b=" + b + ")";
  }
}

See? Instead of annotating the validator method, you just override the interface method. It is just as simple as before, but more type-safe and feels more “standard-ish”. It will also be much easier to handle by AOP, as you are going to find out below.

Controller:

The controller looks just the same as before. You still have the method annotated with @ValidateAction and taking InputRequest as its first parameter.

package de.scrum_master.app;

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

public class MyController {
  @PostMapping("/sum/{platform}")
  @ValidateAction
  public Result sum(@RequestBody InputRequest input, @PathVariable("platform") String platform) {
    System.out.println("input: " + input + ", platform: " + platform);
    return new Result(input.getA() + input.getB());
  }
}

Sample driver application:

package de.scrum_master.app;

public class Application {
  public static void main(String[] args) {
    new MyController().sum(new InputRequest(11, 22), "foo");
  }
}

Aspect:

The aspect is super simple now, like I said in my comment to your question. The pointcut checks for methods which

  • are annotated with @ValidateAction and
  • have a first parameter implementing MyValidator.

Then it binds the MyValidator parameter to an advice method argument by args().

Please note that you can omit the trailing && execution(* *(..)) in Spring AOP because it only supports method execution() joinpoints, while in AspectJ there are also call() joinpoints which here would lead to double validation and log output.

package de.scrum_master.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

import de.scrum_master.app.MyValidator;

@Aspect
@Component
public class MyValidatorAspect {
  @Before("@annotation(de.scrum_master.app.ValidateAction) && execution(* *(..)) && args(validator, ..)")
  public void validateAspect(JoinPoint joinPoint, MyValidator validator) throws Throwable {
    System.out.println(joinPoint);
    validator.validate();
  }
}

Console log:

execution(Result de.scrum_master.app.MyController.sum(InputRequest, String))
Performing custom validation
input: InputRequest(a=11, b=22), platform: foo

Update answering follow-up questions: Please read some documentation. The Spring manual is a good source.

  1. what does it mean && args(validator, ..)?

It is called argument binding. This specific pointcut designator means: Match all target methods where the first argument matches the type of validator in the advice method arguments list and bind the value to that argument. As you see, the argument is declared as MyValidator validator. The , .. means that any subsequent target method arguments (if any) do not matter. For more information see this manual paragraph.

  1. What would happen if more than one class implementing MyValidator interface . I mean how would FW figure out that which implementation has to passed while invoking current controller operation ?

FW meaning what? Maybe framework? Anyway, I do not see the problem. Why would the framework have to figure out which implementation there is? That is the beauty of OOP and virtual methods: You do not need to figure out anything because each implementation has a boolean validate() which then gets called. It is simple, type-safe, hassle-free. Your solution is neither of these. This approach just works. Instead of asking, why don’t you just try?

CLICK HERE to find out more related problems solutions.

Leave a Comment

Your email address will not be published.

Scroll to Top