Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring AOP - annotation with args

I am stuck with a problem in spring boot. I am trying to give extra functionality to some RestControllers, and I am trying to achieve it with some custom annotations. Here is an example.

My annotation:

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

My aspect:

@Aspect
@Component
public class MyAspect {
  @Around(
    value = "@annotation(MyCustomAnnotation)",
    argNames = "proceedingJoinPoint,someArg"
  )
  public Object addMyLogic(ProceedingJoinPoint proceedingJoinPoint, String someArg)
    throws Throwable
  {
    System.out.println(someArg);
    return proceedingJoinPoint.proceed();
  }
}

My method:

@MyCustomAnnotation(someArg = "something")
@GetMapping("/whatever/route")
public SomeCustomResponse endpointAction(@RequestParam Long someId) {
  SomeCustomResult result = someActionDoesNotMatter(someId);
  return new SomeCustomResponse(result);
}

Mostly based on the docs (https://docs.spring.io/spring/docs/3.0.3.RELEASE/spring-framework-reference/html/aop.html - 7.2.4.6 Advice parameters) I am pretty sure, it should work.

I am here, because it does not...

What drives me crazy, is that even Intellij, when tries to help with argNames (empty string -> red underline -> alt+enter -> Correct argNames attribute) gives me this, and keeps it red...

Based on the docs, proceedingJoinPoint is not even needed (it does not work without it either): "If the first parameter is of the JoinPoint, ProceedingJoinPoint..."

With the current setup, it says "Unbound pointcut parameter 'someArg'"

At this point, I should also note, that without the args it is working fine.

I have two questions, actually:

  1. Why does this does not work? (That was pretty obvious)

  2. If I would like to give some extra functionality to some controllers, and I would like to parameterise it from the outside, is it the right pattern in spring boot? (With python, it was quite easy to do this with decorators - I am not quite sure, that I am not misguided by the similar syntax)

One example (the example above was pretty abtract):

I would like to create a @LogEndpointCall annotation, and the developer of a route can later just put it on the endpoint that he is developing

...however, it would be nice, if he could add a string (or more likely, an enum) as a parameter

@LogEndpointCall(EndpointCallLogEnum.NotVeryImportantCallWhoCares)

or

@LogEndpointCall(EndpointCallLogEnum.PrettySensitiveCallCheckItALot)

so that the same logic is triggered, but with a different param -> and a save to a different destination will be made.

like image 924
Balint Toth Avatar asked Oct 15 '25 18:10

Balint Toth


1 Answers

You cannot directly bind an annotation property to an advice parameter. Just bind the annotation itself and access its parameter normally:

@Around("@annotation(myCustomAnnotation)")
public Object addMyLogic(
  ProceedingJoinPoint thisJoinPoint,
  MyCustomAnnotation myCustomAnnotation
)
  throws Throwable
{
  System.out.println(thisJoinPoint + " -> " + myCustomAnnotation.someArg());
  return thisJoinPoint.proceed();
}

It will print the something like this with Spring AOP

execution(SomeCustomResponse de.scrum_master.app.Application.endpointAction(Long)) -> something

and something like this with AspectJ (because AJ also knows call joinpoints, not just execution)

call(SomeCustomResponse de.scrum_master.app.Application.endpointAction(Long)) -> something
execution(SomeCustomResponse de.scrum_master.app.Application.endpointAction(Long)) -> something
like image 61
kriegaex Avatar answered Oct 18 '25 14:10

kriegaex