Skip to content

Clarification: Spring AOP pointcuts match protected methods when CGLIB is used [SPR-15354] #19917

Closed
@spring-projects-issues

Description

@spring-projects-issues

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:

Referenced from: commits b90d3d0, 66670cf

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)type: taskA general task

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions