Description
Alexander Kriegisch opened SPR-15354 and commented
Disclaimer: I am not a Spring user, just an AspectJ expert trying to answer a StackOverflow question related to Spring AOP.
The Spring documentation in chapter 11.2.3 says:
Due to the proxy-based nature of Spring’s AOP framework, protected methods are by definition not intercepted, neither for JDK proxies (where this isn’t applicable) nor for CGLIB proxies (where this is technically possible but not recommendable for AOP purposes). As a consequence, any given pointcut will be matched against public methods only!
If your interception needs include protected/private methods or even constructors, consider the use of Spring-driven native AspectJ weaving instead of Spring’s proxy-based AOP framework. This constitutes a different mode of AOP usage with different characteristics, so be sure to make yourself familiar with weaving first before making a decision.
Anyway, after I set up a little Spring Boot project and tried to use Spring AOP on a class, I saw that at least for CGLIB proxies pointcuts match protected methods. I was really surprised because the documentation says otherwise. So either the documentation is not up to date with this new "feature" or this is a regression - since which version I cannot say.
When debugging into the advice with @EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
and inspecting AopContext.currentProxy()
I also see that a CGLIB proxy is created for a class which only has one protected method and for which the pointcut should not even match, thus not create a proxy.
Just try something like this:
package spring.aop;
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RetryOnFailure {
int attempts() default 2;
long delay() default 1;
TimeUnit unit() default TimeUnit.SECONDS;
}
package spring.aop;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@RestController("/")
public class HomeController {
static int counter = 0;
@RequestMapping
@RetryOnFailure(attempts = 3, delay = 2, unit = TimeUnit.SECONDS)
protected String index() {
throw new RuntimeException("Exception in try " + ++counter);
}
}
package spring.aop;
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 final class MethodRepeater {
@Around("execution(* spring.aop..*(..)) && @annotation(retryOnFailure)")
public Object wrap(final ProceedingJoinPoint joinPoint, RetryOnFailure retryOnFailure) throws Throwable {
System.out.println(joinPoint);
return proceed(joinPoint, retryOnFailure);
}
private Object proceed(ProceedingJoinPoint joinPoint, RetryOnFailure retryOnFailure) throws Throwable {
int attempt = 1;
while (true) {
try {
return joinPoint.proceed();
} catch (final Throwable ex) {
System.out.println("Try #" + attempt + " failed: " + ex);
if (++attempt >= retryOnFailure.attempts())
return "OK";
if (retryOnFailure.delay() > 0L)
retryOnFailure.unit().sleep(retryOnFailure.delay());
}
}
}
}
package spring.aop;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
Affects: 4.3.7
Reference URL: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-pointcuts-designators
Issue Links:
- Cannot intercept calls to protected methods of CGLIB proxied class [SPR-1611] #6308 Cannot intercept calls to protected methods of CGLIB proxied class
- "CglibAopProxy: Unable to proxy method" WARN when bean class contains static final method [SPR-11107] #15733 "CglibAopProxy: Unable to proxy method" WARN when bean class contains static final method
- CglibAopProxy needs to detect package-visible methods when defined in a different ClassLoader [SPR-11618] #16241 CglibAopProxy needs to detect package-visible methods when defined in a different ClassLoader
- Revisit CGLIB AOP proxy warnings for final methods [SPR-15436] #19997 Revisit CGLIB AOP proxy warnings for final methods