Backend Development 8 min read

Mastering Spring Control Flow Pointcuts: A Hands‑On Example

This article explains Spring's control flow pointcuts, compares them with AspectJ cflow pointcuts, walks through a complete Java example with nested service classes, shows how to configure a ControlFlowPointcut and proxy, and discusses performance implications and usage scenarios.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering Spring Control Flow Pointcuts: A Hands‑On Example

Environment: Spring 5.3.23.

1. Introduction

Spring control flow pointcuts are conceptually similar to AspectJ cflow pointcuts, though less powerful. They match the current call stack, firing when a join point is invoked from a specified class or method. The pointcut is defined using org.springframework.aop.support.ControlFlowPointcut .

2. Control Flow Example

Three simple classes are used to demonstrate nested calls: PersonDAO , PersonService , and PersonManager .

<code>static class PersonDAO {
    public void save(String name) {
        System.out.println("PersonDAO save method invoke...");
    }
}

static class PersonService {
    private PersonDAO dao;
    public PersonService(PersonDAO dao) { this.dao = dao; }
    public void save(String name) {
        System.out.println("PersonService save method invoke...");
        this.dao.save(name);
    }
}

static class PersonManager {
    private PersonService ps;
    public void setPs(PersonService ps) { this.ps = ps; }
    public void index(String name) {
        System.out.println("PersonManager index method invoke...");
        this.ps.save(name);
    }
}
</code>

Instantiate the classes and create a proxy for PersonService using a ControlFlowPointcut that matches PersonManager.index :

<code>PersonDAO dao = new PersonDAO();
PersonService target = new PersonService(dao);
PersonManager pm = new PersonManager();
Class<?> clazz = PersonManager.class;
String methodName = "index";
ControlFlowPointcut pointcut = new ControlFlowPointcut(clazz, methodName);
MethodInterceptor logInterceptor = invocation -> {
    System.out.println("before log...");
    Object ret = invocation.proceed();
    System.out.println("after log...");
    return ret;
};
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, logInterceptor);
ProxyFactory factory = new ProxyFactory(target);
factory.addAdvisor(advisor);
factory.setProxyTargetClass(true);
PersonService ps = (PersonService) factory.getProxy();
pm.setPs(ps);
pm.index("张三");
</code>

Console output:

<code>PersonManager index method invoke...
before log...
PersonService save method invoke...
PersonDAO save method invoke...
after log...
</code>

The output shows that the interceptor runs before and after the PersonService.save method because the control flow matches the specified class and method.

Core Methods of ControlFlowPointcut

<code>public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher, Serializable {
    private final Class<?> clazz;
    @Nullable private final String methodName;
    @Override
    public boolean matches(Class<?> clazz) { return true; }
    @Override
    public boolean matches(Method method, Class<?> targetClass) { return true; }
    @Override
    public boolean isRuntime() { return true; }
    @Override
    public boolean matches(Method method, Class<?> targetClass, Object... args) {
        for (StackTraceElement element : new Throwable().getStackTrace()) {
            if (element.getClassName().equals(this.clazz.getName()) &&
                (this.methodName == null || element.getMethodName().equals(this.methodName))) {
                return true;
            }
        }
        return false;
    }
}
</code>

Only when the runtime check returns true does the interceptor execute.

3. Performance

Dynamic pointcuts, including control flow pointcuts, are more expensive to evaluate than static ones because they must consider the current call stack on each method invocation and cannot cache results. In Java 1.4, the cost is roughly five times that of other dynamic pointcuts.

4. Usage Considerations

Control flow pointcuts are useful when you need to apply advice only when a method is invoked within a specific call chain, such as logging or security checks that depend on the caller context.

JavaProxyAOPSpringAspectJControl Flow Pointcut
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.