- Chapter
- Chapter 1 — Cross-Cutting Concerns
- Prerequisites
- Topics 1.1–1.2
- You will be able to
- Define and apply the five AOP concepts, map them to Spring and AspectJ constructs, and choose a weaving strategy
- Covers
- Aspect · Join Point · Pointcut · Advice · Weaving
On this page
- 1. Concept / Definition
- 2. Why It Exists
- 3. Core Concepts
- 4. How It Works Internally
- 4A. Deep Dive — Spring AOP or Full AspectJ?
- 5. APIs / Features / Syntax
- 6. Practical Examples
- 7. Common Problems & Mistakes
- 8. Performance & Production Considerations
- 9. Architecture & Design Considerations
- 10. Interview Questions & Answers
1Concept / Definition
Aspect-Oriented Programming (AOP) is a programming paradigm that modularises cross-cutting concerns. It complements OOP: classes modularise the domain, aspects modularise policies that cut across classes.
| Term | Definition | Spring / AspectJ construct |
|---|---|---|
| Aspect | A module encapsulating a cross-cutting concern: advice + pointcuts | @Aspect class |
| Join point | A well-defined point in program execution where behaviour can be added | Method execution (Spring AOP); also calls, field access, constructors (AspectJ) |
| Pointcut | A predicate that selects join points | @Pointcut("execution(* com.acme..*Service.*(..))") |
| Advice | Action taken at selected join points, and when | @Before, @After, @AfterReturning, @AfterThrowing, @Around |
| Weaving | Linking aspects with target code to create advised objects | Runtime proxies (Spring); compile/load-time (AspectJ) |

Related terms: target object (the object being advised), AOP proxy (the object created to implement the aspect contracts), advisor (Spring: one pointcut + one advice), introduction (adding interfaces to a type).
2Why It Exists
As Topic 1.2 showed, OOP lacks a way for a concern to say where it applies. AOP, developed at Xerox PARC (Gregor Kiczales and colleagues, 1997) and implemented as AspectJ (2001), added that capability as a language extension. Spring adopted a pragmatic subset in 2004: proxy-based method interception, later using AspectJ's annotation style and pointcut language (Spring 2.0) while keeping runtime weaving so that no special compiler is required.
3Core Concepts
3.1 Aspect
@Aspect
@Component
class PerformanceAspect {
private final MeterRegistry meters;
PerformanceAspect(MeterRegistry meters) { this.meters = meters; }
@Pointcut("within(com.acme..service..*) && execution(public * *(..))") // WHERE
void publicServiceMethods() { }
@Around("publicServiceMethods()") // WHAT + WHEN
Object time(ProceedingJoinPoint pjp) throws Throwable {
var sample = Timer.start(meters);
try { return pjp.proceed(); }
finally {
sample.stop(meters.timer("service.calls",
"method", pjp.getSignature().toShortString()));
}
}
}3.2 Join point
| Join point kind | AspectJ | Spring AOP |
|---|---|---|
| Method execution | Yes | Yes (only kind) |
| Method call (at the caller) | Yes | No |
| Constructor execution/call | Yes | No |
| Field get/set | Yes | No |
| Static initialisation | Yes | No |
| Exception handler execution | Yes | No |
3.3 Pointcut
A pointcut is a boolean expression over join points: execution(* com.acme..*Service.*(..)) && @annotation(audited). Pointcuts can be named and combined with &&, ||, !. Details in Topic 2.3.
3.4 Advice

| Advice | Runs |
|---|---|
| Before | Before the join point |
| After returning | After normal completion |
| After throwing | After an exception |
| After (finally) | After either outcome |
| Around | Surrounds the join point; decides whether/how it proceeds |
3.5 Weaving

| Weaving | How | Pros | Cons |
|---|---|---|---|
| Compile-time | ajc compiler | All join points; no runtime overhead for weaving | Special build |
| Post-compile (binary) | Weave existing class files/JARs | Advise third-party code | Build step |
| Load-time (LTW) | Java agent / Spring @EnableLoadTimeWeaving | No special compiler; full AspectJ | JVM agent, class-loader setup |
| Runtime (proxies) | Spring AOP | Zero setup; per-bean | Method execution only; proxy limitations |
4How It Works Internally
In Spring AOP, weaving happens when a bean is created. The auto-proxy creator (a BeanPostProcessor) collects advisors — from @Aspect beans, @Transactional infrastructure, method security and others — tests each bean's class and methods against their pointcuts, and wraps matching beans in a proxy. At call time the proxy builds an interceptor chain for the invoked method (cached per method) and executes it.
| AOP concept | Spring runtime object |
|---|---|
| Aspect | @Aspect bean → several InstantiationModelAwarePointcutAdvisors |
| Pointcut | AspectJExpressionPointcut (uses AspectJ's parser/matcher) |
| Advice | AspectJAroundAdvice, AspectJMethodBeforeAdvice, … adapted to MethodInterceptor |
| Join point | MethodInvocation / ProceedingJoinPoint |
| Weaving | AbstractAutoProxyCreator.wrapIfNecessary → ProxyFactory |
4ADeep Dive — Spring AOP or Full AspectJ?
| Decision factor | Spring AOP is enough | Consider AspectJ |
|---|---|---|
| Targets | Spring beans | Domain objects created with new, JPA entities, third-party classes |
| Join points | Public (or class-proxy-visible) method executions | Field access, constructors, calls, private methods |
| Self-invocation | Acceptable to restructure | Must be advised |
| Performance | Microsecond overhead fine | Very hot, fine-grained methods |
| Build/deploy | Keep simple | Team can manage ajc or an agent |
Spring can use AspectJ weaving for its own infrastructure too: @EnableTransactionManagement(mode = AdviceMode.ASPECTJ) and @EnableCaching(mode = ASPECTJ) switch from proxies to AspectJ-woven aspects, which removes self-invocation and visibility limits at the cost of weaving setup.
<!-- Load-time weaving: META-INF/aop.xml -->
<aspectj>
<weaver options="-Xlint:ignore">
<include within="com.acme..*"/>
</weaver>
<aspects>
<aspect name="com.acme.audit.DomainAuditAspect"/>
</aspects>
</aspectj>5APIs / Features / Syntax
| API | Purpose |
|---|---|
@Aspect, @Pointcut | Declare aspects and named pointcuts |
@Before, @After, @AfterReturning, @AfterThrowing, @Around | Advice |
JoinPoint, ProceedingJoinPoint | Join point access |
@EnableAspectJAutoProxy | Enable @Aspect processing (Boot auto-configures) |
@EnableLoadTimeWeaving, aop.xml | AspectJ load-time weaving |
ProxyFactory, Advisor, MethodInterceptor | Programmatic Spring AOP |
6Practical Examples
6.1 Programmatic AOP without annotations
var factory = new ProxyFactory(new PriceServiceImpl());
factory.addAdvice((MethodInterceptor) inv -> {
log.debug("-> {}", inv.getMethod().getName());
return inv.proceed();
});
PriceService proxied = (PriceService) factory.getProxy();
proxied.price("SKU-1"); // logs, then calls the target6.2 Aspect with annotation-based pointcut
@Aspect @Component
class ReadOnlyGuard {
@Before("@annotation(com.acme.ReadOnlyOperation) && args(.., cmd)")
void guard(Object cmd) {
if (Maintenance.isOn()) throw new MaintenanceModeException();
}
}6.3 Introductions — adding an interface to existing beans
public interface Versioned { String version(); }
public class DefaultVersioned implements Versioned {
public String version() { return "v1"; }
}
@Aspect @Component
class VersioningIntroduction {
// every matching bean now also implements Versioned
@DeclareParents(value = "com.acme.api..*Controller+",
defaultImpl = DefaultVersioned.class)
public static Versioned mixin;
}
// ((Versioned) ctx.getBean(OrderController.class)).version() → "v1"Introductions are the least used AOP feature; they are shown here to complete the vocabulary. Prefer ordinary interfaces unless you must retrofit behaviour onto classes you cannot change.
6.4 Testing an aspect in isolation
@Test
void timesServiceCalls() {
var registry = new SimpleMeterRegistry();
var factory = new AspectJProxyFactory(new PriceServiceImpl());
factory.addAspect(new PerformanceAspect(registry));
PriceService proxy = factory.getProxy();
proxy.price("SKU-1");
assertThat(registry.find("service.calls").timer().count()).isEqualTo(1);
}AspectJProxyFactory applies an @Aspect instance to a target without starting a Spring context — the fastest way to test pointcut matching and advice behaviour together.
6.5 Mapping the vocabulary to familiar annotations
| Annotation you already use | Join point | Pointcut | Advice kind |
|---|---|---|---|
@Transactional | Method execution | Methods/classes annotated @Transactional | Around |
@Cacheable | Method execution | Annotated methods | Around (may skip the call) |
@PreAuthorize | Method execution | Annotated methods | Before (throws to deny) |
@Async | Method execution | Annotated methods | Around (submits to executor) |
@Timed / @Observed | Method execution | Annotated methods | Around |
7Common Problems & Mistakes
- Expecting Spring AOP to advise non-bean objects. Only container-managed proxies are advised.
- Pointcuts that match too much.
execution(* *(..))advises every bean method, including framework beans. - Confusing call and execution join points. Spring advises execution in the target via the proxy.
- Putting business rules into aspects. Aspects should implement policies.
- Ignoring advice ordering. Several aspects on one method need explicit
@Order.
Most of these mistakes show up as an aspect that silently does nothing, or does far too much. When advice never runs, check three things in order: that the target is a Spring bean obtained from the container rather than created with new, that the call enters through the proxy rather than via this, and that the method is neither final nor private, because a CGLIB subclass cannot override either. Logging the bean's class at start-up, or calling AopUtils.isAopProxy, confirms whether a proxy exists at all. Over-broad pointcuts fail differently: execution(* *(..)) also matches framework and configuration beans, which slows start-up, creates early proxies that trigger 'not eligible for auto-proxying' log messages and can even cause circular-reference errors. A further trap is throwing a checked exception from @Around advice that the target method does not declare; the proxy then wraps it in an UndeclaredThrowableException that callers never expect. Finally, aspects without an explicit order run in an undefined sequence, so give each custom aspect an @Order relative to the transaction and security advisors.
8Performance & Production Considerations
| Concern | Guidance |
|---|---|
| Proxy overhead | One interceptor chain per call; negligible for service methods |
| Pointcut matching | Done at proxy creation and cached per method — keep pointcuts specific to limit start-up work |
| AspectJ weaving | No proxy hop; LTW adds class-loading time |
| Debuggability | Stack traces show interceptor frames; name aspects clearly |
9Architecture & Design Considerations
- AOP as a second modularity axis complements, not replaces, good domain design.
- Annotation-driven pointcuts are the most maintainable:
@annotation(Audited)is explicit at the target; package-wideexecutionpointcuts are powerful but invisible. - Aspects are part of the architecture. Document them and include them in reviews.
- Prefer framework-provided aspects (transactions, caching, security, observation) before writing custom ones.
10Interview Questions & Answers
What is AOP?
Aspect-Oriented Programming is a paradigm for modularising cross-cutting concerns: behaviour such as logging, transactions, security checks, metrics and auditing that would otherwise be repeated across many classes. OOP modularises the domain into classes, but a policy like 'every public service method is timed' has no natural home there, so the code ends up tangled with business logic and scattered across the codebase. AOP lets you write that policy once as an aspect, declare where it applies with a pointcut, and let the framework attach it. In Spring it is not an exotic extra: @Transactional, @Cacheable, @PreAuthorize and @Async are all implemented with AOP proxies, so every Spring developer already relies on it daily.
Define join point, pointcut and advice.
A join point is a well-defined point in program execution where extra behaviour could be attached. AspectJ supports many kinds, such as method calls, field access and constructor execution, but in Spring AOP a join point is always a method execution on a Spring bean. A pointcut is a predicate that selects a subset of join points, written in AspectJ's expression language, for example execution(* com.acme..*Service.*(..)) or @annotation(Audited). Advice is the code that runs at the selected join points, together with when it runs: before, after returning, after throwing, after in the finally sense, or around. In short, join points are the candidates, the pointcut chooses where, the advice says what and when.
What is weaving and which kind does Spring AOP use?
Weaving is the process of linking aspects with target code so that advice actually runs at the matched join points. AspectJ can weave at compile time with the ajc compiler, post-compile into existing class files and JARs, or at load time through a Java agent that rewrites classes as they are loaded. Spring AOP weaves at runtime and never modifies the bytecode of your classes: when a bean is created, the auto-proxy creator wraps it in a JDK dynamic proxy or a CGLIB subclass, and that proxy runs the advice chain. This is why Spring AOP needs no special build step or agent, and also why it only affects calls that go through the proxy.
What join points does Spring AOP support?
Only method execution join points, and only on Spring-managed beans when the call passes through the proxy. With JDK dynamic proxies, used when proxying interfaces, that means interface methods. With CGLIB class proxies, which Boot uses by default, public and protected methods can be advised, and even package-visible ones if necessary, but final, private and static methods cannot, because the generated subclass cannot override them. Constructors, field reads and writes, static initialisers and calls made inside the same object through this are invisible to Spring AOP. If a requirement needs any of those, for example advising JPA entities or self-invocations, you need AspectJ compile-time or load-time weaving instead of proxies.
What is an advisor in Spring?
An advisor is Spring's own building block that pairs exactly one pointcut with exactly one advice; AspectJ has no equivalent term. It is what the auto-proxy creator actually works with: for each bean it collects every Advisor in the context, asks each pointcut whether it matches the bean's class and methods, and builds a proxy from the matching advisors. An @Aspect class is not used directly; ReflectiveAspectJAdvisorFactory turns each advice method into its own advisor, so an aspect with three advice methods becomes three advisors. Infrastructure contributes advisors too, such as BeanFactoryTransactionAttributeSourceAdvisor for @Transactional and the method-security advisors for @PreAuthorize, which is why their ordering can be controlled like any aspect.
When would you choose AspectJ weaving over Spring AOP?
I choose AspectJ only when a concrete requirement exceeds what proxies can do. Typical triggers are advising objects that Spring does not create, such as JPA entities or domain objects built with new, needing non-method join points like field access or constructors, advising private methods, or requiring self-invocations to be intercepted, for example a @Transactional method called from another method in the same class. Very hot, fine-grained methods where the proxy hop is measurable are another reason. The price is a special build step with ajc, or a load-time weaving agent and class-loader setup, plus tooling and debugging that fewer developers know. Before switching, I always check whether splitting the bean solves the problem.
How do you keep custom aspects maintainable?
I treat each custom aspect as a small platform feature with an owner. The pointcut should be explicit at the target, ideally a marker annotation such as @Audited, so a reader of the method can see that behaviour is attached; package-wide execution pointcuts are powerful but invisible in code review. Advice stays thin and delegates to an ordinary service bean that can be unit tested. Ordering relative to transactions and security is declared with @Order and documented, because whether auditing runs inside or outside the transaction changes behaviour on rollback. I test pointcut matching and advice together with AspectJProxyFactory, and add ArchUnit rules that stop business rules creeping into aspects over time.
What role does AOP play in an enterprise platform?
AOP is the mechanism through which platform policies reach application code without every team reimplementing them. Transactions, method security, caching, observation with @Observed, the resilience annotations for retry and concurrency limits added in Framework 7, and audit trails are all applied as advice around business methods. That gives a platform team a single place to change behaviour: a new tracing attribute or audit field is added once in an aspect or starter and appears across every service after an upgrade. The architectural risk is invisibility, so I would favour framework-provided aspects, require explicit annotations for custom policies, document ordering, and keep business decisions out of aspects so domain logic stays readable.