0

I have created a simple custom annotation in Spring Boot that logs something and it's working but only for the first annotation, the nested one are not called

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

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Traceable {

}

Annotation processor (aspect)

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class TraceableAspect {

    @Around("@annotation(Traceable)")
    public Object trace(ProceedingJoinPoint joinPoint) throws Throwable {

        System.out.println("Inside Aspect");
        
        Object result = joinPoint.proceed();

        System.out.println(result);
        return result;
    }
}

Example of a controller used to test

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/test")
public class ControllerTest {

    @GetMapping("/get")
    @Traceable
    public String get(){

        test1();
        test2();

        return "Hi";
    }
    
    @Traceable
    public void test1(){
        String str = "1";
        System.out.println(str);
    }

    @Traceable
    public Object test2(){
        String str = "2";
        System.out.println(str);

        test1();
        return null;
    }

}

The console output here is:

Inside Aspect
1
2
1
Hi

but I think it's worng, it has to be like this:

    Inside Aspect
    Inside Aspect
    1
    Inside Aspect
    2
    Inside Aspect
    1
    Hi

It seems that only the first @Traceable is processed, all the other ones are ignored. How to handle this? Thanks

E-Riz
  • 31,431
  • 9
  • 97
  • 134
Ares91
  • 506
  • 2
  • 8
  • 27

1 Answers1

1

To understand why you're getting the results you see, you have to understand how Spring implements handling of most behavioral annotations: using proxies. That means that only method calls that come through the proxy get the annotation behavior. That's the typical scenario when one object calls a reference it has to another object; the reference is actually to the proxy, which intercepts the call and wraps the behavior (in this case, logging) around it. However, when calling methods within the same instance, there is no proxy in play (Spring doesn't/can't replace this with a reference to the proxy), and thus no annotation behavior.

There are a few ways to work around that, some ideas are discussed in this SO Q&A. There are also some answers here with options to work around the proxying limitation.

E-Riz
  • 31,431
  • 9
  • 97
  • 134