Open-Source Wikis

/

Spring Framework

/

Features

/

AOP (Aspect-Oriented Programming)

spring-projects/spring-framework

AOP (Aspect-Oriented Programming)

Active contributors: Juergen Hoeller, Sam Brannen

Spring's AOP capabilities turn declarative annotations like @Transactional, @Async, @Cacheable, @PreAuthorize, and @Validated into runtime behavior. This page explains how the framework's AOP machinery works and how the major annotation-driven features plug into it.

Two implementations: proxies vs weaving

Spring offers two different AOP implementations:

Feature spring-aop (proxy) spring-aspects (AspectJ weaving)
Mechanism Runtime proxy generation (JDK or CGLIB) Compile-time or load-time bytecode rewriting
Setup None — automatic when annotations are used AspectJ compiler or -javaagent:spring-instrument.jar
Self-invocation ❌ Bypassed ✅ Works
Final classes/methods ❌ Cannot proxy ✅ Works
Performance overhead One additional method call per intercepted method Inlined into the method
GraalVM native image First-class Trickier

The proxy approach is the default and is what most apps use. AspectJ weaving is opt-in for cases where proxies don't fit.

Proxy AOP: how it works

graph LR
    BPP["BeanPostProcessor: AnnotationAwareAspectJAutoProxyCreator"]
    BEAN[Original bean] -->|"postProcessAfterInitialization"| BPP
    BPP -->|"determine matching advisors"| ADVISORS[Advisor list]
    BPP -->|"if advisors > 0"| FACTORY[ProxyFactory.getProxy]
    FACTORY -->|"target has interfaces"| JDK[JDK Dynamic Proxy]
    FACTORY -->|"target is a class"| CG[CGLIB Subclass]
    JDK --> RESULT[Proxied bean replaces original]
    CG --> RESULT

Every annotation that triggers AOP (@EnableTransactionManagement, @EnableAsync, @EnableCaching, @EnableAspectJAutoProxy) imports the same BeanPostProcessor, AnnotationAwareAspectJAutoProxyCreator, plus the appropriate Advisor beans for the feature.

When BeanPostProcessors run during the bean lifecycle, the auto-proxy creator checks if any registered Advisor matches the bean's class/methods. If so, it returns a proxy in place of the bean. Other beans that depend on this one will receive the proxy.

Method invocation through the proxy

sequenceDiagram
    participant Caller
    participant Proxy
    participant Chain as AdvisorChain (MethodInterceptor list)
    participant Target as target bean
    Caller->>Proxy: instance.method(args)
    Proxy->>Proxy: build chain for this method (cached)
    Proxy->>Chain: chain.proceed
    Chain->>Chain: ExposeInvocationInterceptor (sets ThreadLocal)
    Chain->>Chain: TransactionInterceptor (begins tx)
    Chain->>Chain: CacheInterceptor (checks cache)
    Chain->>Chain: ... other matched advice ...
    Chain->>Target: target.method(args)
    Target-->>Chain: result
    Chain-->>Proxy: result
    Proxy-->>Caller: result

The chain is per method, not per call — ProxyMethodInvocation caches the resolved interceptor list for each method.

Annotation-driven features

@Transactional

  • Defined in spring-tx
  • Implemented as TransactionInterceptor (a MethodInterceptor)
  • Pointcut: TransactionAttributeSourcePointcut matches methods/classes carrying @Transactional
  • Around-advice: begins a transaction, calls proceed(), commits or rolls back based on the outcome

@Async

  • Defined in spring-context
  • Implemented as AsyncExecutionInterceptor
  • Submits the actual invocation to a TaskExecutor and returns a Future (or void)
  • Importer: @EnableAsync

@Cacheable / @CachePut / @CacheEvict

  • Defined in spring-context (cache abstraction)
  • Implemented as CacheInterceptor
  • Around-advice: looks up cache, returns cached value or invokes target then stores
  • Importer: @EnableCaching

@PreAuthorize / @PostAuthorize

  • Defined in Spring Security (a separate project)
  • Built on the same AnnotationAwareAspectJAutoProxyCreator infrastructure here

@Validated (method validation)

  • Defined in spring-context (org.springframework.validation.beanvalidation.MethodValidationInterceptor)
  • Validates @Valid parameters and return values using JSR-303 / Bean Validation

Custom advice

To add your own around-advice:

  1. Implement org.aopalliance.intercept.MethodInterceptor (or use @Aspect style):

    public class TimingInterceptor implements MethodInterceptor {
        public Object invoke(MethodInvocation invocation) throws Throwable {
            long start = System.nanoTime();
            try {
                return invocation.proceed();
            } finally {
                long elapsed = System.nanoTime() - start;
                // record metric
            }
        }
    }
  2. Pair with a pointcut to form an Advisor:

    @Bean
    public Advisor timingAdvisor() {
        AspectJExpressionPointcut p = new AspectJExpressionPointcut();
        p.setExpression("execution(* com.example..*Service.*(..))");
        return new DefaultPointcutAdvisor(p, new TimingInterceptor());
    }
  3. Annotate a @Configuration with @EnableAspectJAutoProxy to install the auto-proxy creator (Spring Boot does this automatically when AOP is on the classpath).

@Aspect style

Alternatively, write a class annotated @Aspect with @Pointcut and @Around (or @Before, etc.) methods:

@Aspect
@Component
public class TimingAspect {
    @Around("execution(* com.example..*Service.*(..))")
    public Object time(ProceedingJoinPoint pjp) throws Throwable { ... }
}

AspectJAdvisorFactory extracts advisors from @Aspect beans at startup.

Limitations of proxy-based AOP

These bite reliably enough that the framework's documentation calls them out repeatedly:

  1. Self-invocation is not advised. Calling another @Transactional method on this from within the same bean does not go through the proxy. The recommendation is to inject the proxy of yourself (Spring 4.3+ supports self-injection by type) or restructure so the call crosses bean boundaries.
  2. final classes cannot be CGLIB-proxied; final methods cannot be advised. Make the class non-final or extract the method into an interface.
  3. private and package-private methods are not advised by proxies.
  4. Constructors can't be advised.
  5. Calls from within the framework's own infrastructure (e.g., BeanPostProcessor callbacks during initialization) may not see proxies that are themselves still being initialized.

If any of these are a problem, switch to AspectJ via spring-aspects.

AOT and AOP

For native-image builds, proxies must be registered as runtime hints. The framework's auto-proxy creator implements BeanRegistrationAotContributor to ensure each proxy class is reachable in the native image. Custom Advisor beans are picked up automatically.

See also

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

AOP (Aspect-Oriented Programming) – Spring Framework wiki | Factory