Spring AOP & Cross-Cutting Concerns

AOP Core Concepts

18 min Lesson 2 of 13

AOP Core Concepts

Aspect-Oriented Programming introduces a small, precise vocabulary. Every conversation you have about AOP — every Spring annotation, every debugger trace — uses these five terms: aspect, join point, advice, pointcut, and weaving. Nail them now and the rest of the tutorial becomes mechanical. Confuse them and everything gets blurry.

Why a shared vocabulary matters: AOP concepts were formalised in the early 2000s and are consistent across AspectJ, Spring AOP, PostSharp (.NET), and other frameworks. Learning them once transfers everywhere.

Aspect

An aspect is the modular unit that encapsulates a cross-cutting concern. Think of it as the AOP equivalent of a class: it bundles related advice and pointcut definitions together, just as a class bundles related fields and methods.

In Spring you declare an aspect with two annotations on a plain Spring bean:

import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; @Aspect // marks this class as an AOP aspect @Component // registers it as a Spring bean so Spring can proxy it public class AuditAspect { // advice + pointcut definitions go here }

Without @Component (or an equivalent registration), Spring never instantiates the class and the aspect has no effect. Without @Aspect, Spring ignores the advice annotations inside it. Both annotations are required.

Join Point

A join point is any identifiable point in your program's execution where advice could be applied. Spring AOP supports exactly one kind of join point: method execution. Every time a Spring-managed bean's method is called, that invocation is a join point.

This is the key Spring AOP constraint compared with full AspectJ: you cannot intercept field reads, object construction, or static method calls in pure Spring AOP. In practice, method execution covers 95 % of real cross-cutting needs (logging, security checks, transactions, caching), so the limitation rarely hurts.

Mental model: picture every method on every Spring bean as a door. A join point is any one of those doors. Advice is the bouncer who decides what happens when the door opens. A pointcut is the rule that says which doors get a bouncer.

At runtime, when advice fires, Spring wraps the join point in a JoinPoint object (from the org.aspectj.lang.JoinPoint interface) that you can inspect:

import org.aspectj.lang.JoinPoint; // inside an aspect method public void logBefore(JoinPoint jp) { String method = jp.getSignature().getName(); // "createOrder" Object[] args = jp.getArgs(); // method arguments String target = jp.getTarget().getClass().getName(); // "com.example.OrderService" }

Advice

Advice is the code that actually runs at a join point — the what and when of the interception. Spring AOP supports five advice types, each corresponding to a different moment relative to the method execution:

  • @Before — runs before the target method. Cannot prevent the method from running (use @Around for that).
  • @AfterReturning — runs after the method returns normally. Receives the return value.
  • @AfterThrowing — runs if the method throws an exception. Receives the exception object.
  • @After — runs after the method finishes, whether normally or with an exception (like a finally block).
  • @Around — wraps the entire invocation; you call proceed() to invoke the real method (covered in depth in lesson 6).

A quick example of @Before advice in context:

import org.aspectj.lang.annotation.Before; import org.aspectj.lang.JoinPoint; @Before("execution(* com.example.service.*.*(..))") public void logMethodEntry(JoinPoint jp) { System.out.println("Entering: " + jp.getSignature().toShortString()); }

The string argument to @Before is a pointcut expression — which brings us to the next concept.

Pointcut

A pointcut is a predicate (a Boolean expression) that selects a subset of all join points. Where advice answers what to do, a pointcut answers where to do it. The two are deliberately separate: you can reuse a pointcut across multiple pieces of advice, and you can change one without touching the other.

Spring AOP uses AspectJ pointcut expression language. The most common designator is execution:

// Matches any public method in any class inside the service package execution(public * com.example.service.*.*(..)) // Matches any method named "find*" in OrderRepository, any return type, any args execution(* com.example.repository.OrderRepository.find*(..)) // Matches any method on beans annotated with @Transactional @annotation(org.springframework.transaction.annotation.Transactional)

The recommended practice is to extract named pointcuts with @Pointcut so they can be reused and composed:

import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.AfterReturning; @Aspect @Component public class AuditAspect { // Named, reusable pointcut @Pointcut("execution(* com.example.service.*.*(..))") public void serviceLayer() {} // method body is always empty // Two pieces of advice reuse the same pointcut @Before("serviceLayer()") public void logEntry(JoinPoint jp) { System.out.println(">> " + jp.getSignature().getName()); } @AfterReturning(pointcut = "serviceLayer()", returning = "result") public void logExit(JoinPoint jp, Object result) { System.out.println("<< " + jp.getSignature().getName() + " returned: " + result); } }
Empty method body is intentional: the method annotated with @Pointcut is never actually executed. It is purely a named anchor that the expression language can reference. The method must exist and must return void — the name is what matters.

Weaving

Weaving is the process of linking aspects to the application objects they affect — the mechanical step that makes interception happen. There are three possible times weaving can occur:

  1. Compile-time weaving (CTW): the AspectJ compiler (ajc) modifies bytecode during compilation. The resulting .class files already contain the advice code inline. Fastest at runtime; requires the AspectJ compiler in the build chain.
  2. Load-time weaving (LTW): a Java agent rewrites bytecode as classes are loaded by the classloader. No compiler change needed; adds startup cost.
  3. Runtime weaving (proxy-based): the framework creates a proxy object at startup that wraps the real bean. This is what Spring AOP uses exclusively. No bytecode modification; works only for Spring-managed beans; limited to method execution join points.

Spring's runtime weaving means the weaving happens once when the application context starts, not on every method call. When Spring instantiates a bean that is matched by at least one pointcut, it silently replaces the plain bean reference with a proxy. Callers interact with the proxy, which runs advice and then delegates to the real object. From the caller's perspective, nothing changes — they still get back the interface or class they asked for.

Self-invocation does not work with Spring AOP proxies. If a bean calls its own method internally (e.g., this.someMethod()), the call bypasses the proxy entirely and no advice fires. This is the most common Spring AOP gotcha. The fix is to inject the bean into itself via @Autowired on a field, or to use full AspectJ weaving. Lesson 9 covers this in detail.

Putting It All Together

Here is a concrete scenario that uses all five concepts at once. The requirement: log every public method call in the com.example.service package.

import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; @Aspect // (1) this class is an ASPECT @Component public class MethodLoggingAspect { // (3) POINTCUT — selects which join points to intercept @Pointcut("execution(public * com.example.service.*.*(..))") public void publicServiceMethods() {} // (2) every public service method is a potential JOIN POINT // (4) ADVICE — what to do at matching join points @Before("publicServiceMethods()") public void logCall(JoinPoint jp) { System.out.printf("[AUDIT] %s called with %d arg(s)%n", jp.getSignature().toShortString(), jp.getArgs().length); } // (5) WEAVING — Spring creates proxy beans at startup; // logCall() fires before every matching method }

Summary

The five AOP concepts form a clean mental chain: an aspect groups related cross-cutting logic; a join point is any eligible interception point (in Spring AOP: any Spring bean method call); advice is the code that runs at a join point and defines when (before, after, around); a pointcut is the expression that selects which join points trigger which advice; and weaving is the process — in Spring's case, proxy creation at startup — that wires aspects into the running application. The next lesson turns these concepts into working code with your first complete aspect.