Spring AOP is applied using proxies, when you call a method of the bean from outside, the proxy is used and the method could be intercepted, but when you call the method from inside the class, the proxy is not used and the class is used directly.
You have three options
The first and easy one, if you do not have problems using public methods is to move the functions b()
, c()
, and d()
to another bean. This way each call to this methods would be intercepted.
public class Test {
public String a() { ... }
}
public class Test2 {
public String b() { ... }
public String c() { ... }
public String d() { ... }
}
You can also use it as inner static class if you want to keep all in the same file.
public class Test {
public String a() { ... }
public static class Test2 {
public String b() { ... }
public String c() { ... }
public String d() { ... }
}
}
You should autowire Test2 in the constructor of Test.
public class Test {
private final Test2 test2;
@Autowired public Test(final Test2 test2) {
this.test2 = test2;
}
public String a() {
test2.b();
test2.c();
test2.d();
}
}
And finally create the around method.
@Around(value = "execution(* package.of.the.class.Test.*(..))")
public Object aroundA(ProceedingJoinPoint pjp) throws Throwable { ... }
@Around(value = "execution(* package.of.the.class.Test2.*(..))")
public Object aroundBCD(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
Object output = pjp.proceed();
long elapsedTime = System.currentTimeMillis() - start;
// perform side efects with elapsed time e.g. print, store...
return output;
}
Or something like
@Around(value = "execution(* package.of.the.class.Test.*(..)) || " +
"execution(* package.of.the.class.Test2.*(..))")
public Object aroundABCD(ProceedingJoinPoint pjp) throws Throwable { ... }
The second option is to use a CGLIB bean, package private methods and self injection.
You declare a CGLIB bean just using the scope annotation
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
@Bean public Test test() {
return new Test();
}
Self injection and package private methods as follows
public class Test {
@Autowired private Test test;
// ...
public String a() {
test.b(); // call through proxy (it is intercepted)
}
String b() { ... } // package private method
// ...
}
The third solution is to use LWT Load-Time weaving that is using aspectj instead of the spring aop. This allows to intercept method calls even inside the same class. You can use the official spring documentation to implement it, but you will have to use a java agent to run.
Calling method
If you need to know if an specific method made the call to the intercepted function, you can use Thread.currentThread().getStackTrace()
for the options 1 or 2. If you use aspectj (option 3), you could intercept the methods with cflow()
.