Spring AOP Enterprise Guide: Pointcut Expressions, Five Advice Types, and Dynamic Proxies Explained

Spring AOP decouples cross-cutting concerns such as logging, authorization, transactions, and performance monitoring from business code, reducing duplication and tight coupling. This guide focuses on annotation-driven configuration, pointcut expressions, five advice types, dynamic proxy internals, and production-ready examples for audit logging and permission checks. Keywords: Spring AOP, dynamic proxy, aspect-oriented programming.

Technical Specification Snapshot

Parameter Description
Core Technology Spring AOP
Language Java
Runtime Environment Spring Boot
Implementation Standard AspectJ annotation style
Weaving Method Runtime weaving
Proxy Mechanism JDK Dynamic Proxy / CGLIB
GitHub Stars Not provided in the source content
Core Dependency spring-boot-starter-aop

Spring AOP is the standard approach for decoupling enterprise cross-cutting concerns

In enterprise applications, business methods often end up handling logging, authorization, transactions, and monitoring at the same time. If you write all of that directly in Services or Controllers, the codebase grows quickly and becomes difficult to maintain.

The value of AOP is not that it replaces OOP. Its value is that it complements OOP by handling cross-cutting concerns more elegantly. Spring AOP injects enhancement logic before and after method calls through proxy objects, keeping business code as clean as possible.

Spring AOP core terms require a shared understanding first

  • JoinPoint: An execution point that can be intercepted. In Spring, this is primarily a method execution.
  • Pointcut: The specific set of methods that should actually be intercepted.
  • Advice: The enhancement logic executed after interception.
  • Aspect: A combination of pointcuts and advice.
  • Proxy: The proxy object generated by Spring at runtime.
  • Weaving: The process of applying aspect logic to the target object.

<dependency>
    <!-- Core Spring AOP dependency; Spring Boot auto-configures it -->

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

This dependency enables AOP support in Spring Boot and serves as the minimal entry point for annotation-driven aspect development.

Annotation-driven configuration is the mainstream way to implement Spring AOP

In modern Spring Boot projects, developers rarely write complex XML for AOP by hand. After adding the dependency, you can define aspects with annotations such as @Aspect, @Component, and @Pointcut.

Common advice annotations include @Before, @After, @AfterReturning, @AfterThrowing, and @Around. Among them, @Around is the most powerful because it can control the full invocation chain.

Pointcut expressions determine whether the enhancement scope is precise

The most commonly used expression is execution, which covers nearly 90% of day-to-day interception scenarios. Incorrect package names, class names, or method signatures are among the most common reasons AOP appears not to work.

@Aspect
@Component
public class LogAspect {

    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceMethods() {}

    @Before("serviceMethods()")
    public void beforeAdvice(JoinPoint joinPoint) {
        // Print the method name and arguments for auditing and troubleshooting
        System.out.println("方法名: " + joinPoint.getSignature().getName());
        System.out.println("参数: " + java.util.Arrays.toString(joinPoint.getArgs()));
    }
}

This example shows the most basic form of @Before advice and is useful for logging or parameter validation before business logic executes.

Common pointcut expressions can be selected by scenario

Expression Purpose Typical Scenario
execution(...) Matches by method signature General interception for Services or Controllers
within(...) Matches by class or package Unified enhancement for a specific layer
@annotation(...) Matches by annotation Audit logging, authorization control
args(...) Matches by parameter type Intercepting methods with specific input parameters
bean(...) Matches by Bean name Precise interception of a single component

Five advice types cover most enhancement scenarios

@Before advice works well for authorization pre-checks and parameter validation. @After advice fits cleanup work. @AfterReturning is useful for recording results. @AfterThrowing is suitable for alerting. @Around is ideal for performance monitoring, caching, and transaction-like control.

@Around("serviceMethods()")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
    long start = System.currentTimeMillis();
    // Record the time before execution for performance analysis
    System.out.println("进入方法: " + pjp.getSignature().getName());

    Object result = pjp.proceed(); // Critical: you must call this, or the target method will not execute

    System.out.println("耗时: " + (System.currentTimeMillis() - start) + "ms");
    return result;
}

This @Around advice can be used directly for slow-method monitoring and is one of the most practical advice types in production systems.

The execution order of the five advice types is important

On normal return: before @Around logic → @Before → business method → @AfterReturning@After → after @Around logic.

On exception: before @Around logic → @Before → business method throws exception → @AfterThrowing@After. If the @Around advice catches the exception manually, the observed behavior depends on the implementation details.

Custom annotations combined with AOP are better for governing business rules

Compared with hardcoding package paths, annotation-based aspects are more stable. They let the business method decide whether enhancement applies, making them ideal for operation logs, authorization, idempotency, and audit tracing.

@Target(java.lang.annotation.ElementType.METHOD)
@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
public @interface OperationLog {
    String value() default ""; // Operation description
    String type() default "查询"; // Operation type
}

This annotation defines business metadata and gives the aspect a way to read the operation name and type.

@Aspect
@Component
public class OperationLogAspect {

    @Pointcut("@annotation(com.example.anno.OperationLog)")
    public void logPointcut() {}

    @Around("logPointcut()")
    public Object logAround(ProceedingJoinPoint pjp) throws Throwable {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        OperationLog log = signature.getMethod().getAnnotation(OperationLog.class);
        // Print annotation metadata to produce structured operation logs
        System.out.println("操作: " + log.value() + ", 类型: " + log.type());
        return pjp.proceed();
    }
}

This aspect shows how to read metadata from method annotations and generate unified audit logs at runtime.

Enterprise projects typically use AOP for logging, authorization, and performance monitoring

A unified API logging aspect can collect the URL, method name, parameters, return value, and execution time. This is especially useful in business services behind an API gateway. An authorization aspect keeps Controllers clean so they can focus only on request and response handling.

@Before("@annotation(com.example.anno.RequirePermission)")
public void checkPermission(JoinPoint joinPoint) {
    String userPermission = "admin"; // Example only; real projects should read this from a token or context
    MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    RequirePermission permission = signature.getMethod().getAnnotation(RequirePermission.class);
    if (!permission.value().equals(userPermission)) {
        throw new RuntimeException("权限不足,无法访问"); // Block the invocation immediately when permission does not match
    }
}

This example demonstrates a typical annotation-driven approach for method-level authorization control.

Dynamic proxy internals define the capability boundaries of Spring AOP

Spring AOP is based on runtime proxies rather than compile-time weaving. If the target class implements an interface, Spring can use a JDK dynamic proxy. If it does not, Spring typically uses CGLIB. In Spring Boot 2.x and later, the framework generally favors CGLIB more often.

This also explains two common limitations. First, Spring AOP primarily intercepts method-level invocations. Second, private methods, final methods, and self-invocation within the same class usually cannot be enhanced as expected.

Most AOP failures come from misunderstanding the proxy model rather than framework defects

An internal call such as this.method() bypasses the proxy object, so the aspect does not run. The usual solutions are to retrieve the proxied Bean from the Spring container or call indirectly through AopContext.currentProxy().

In addition, forgetting to call proceed() inside @Around advice prevents the target method from running at all. A malformed pointcut expression leads to the classic symptom of no error being thrown while the enhancement silently never executes.

FAQ

Q1: Why did I define an aspect, but the method was not intercepted?

A: Check these three items first: whether spring-boot-starter-aop is included, whether the pointcut expression matches the target method, and whether the invocation goes through the Spring proxy rather than an internal this call.

Q2: What is the core difference between Spring AOP and AspectJ?

A: Spring AOP mainly relies on runtime proxies. It is lightweight and easy to integrate, but its capabilities are concentrated at the method interception level. AspectJ supports more powerful compile-time or load-time weaving and covers a broader range of join points.

Q3: Which advice type is most recommended in production?

A: Prefer @Around for performance monitoring, logging, and unified governance. If the requirement is simple, @Before, @AfterReturning, or @AfterThrowing is usually more concise. The principle is to use the lightest mechanism that fully solves the problem.

AI Readability Summary: This guide systematically reconstructs enterprise Spring AOP practices, covering core terminology, pointcut expressions, five advice types, annotation-driven configuration, dynamic proxy internals, logging and authorization examples, and common failure scenarios to help Java developers build a practical AOP methodology quickly.