Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 37

Spring AOP: Implementing Cross-Cutting Concerns with Aspects

Recommended resources

Hello! Welcome back to your interview preparation series.

In our last lesson, we explored how to manage an application's behavior across different environments using externalized configuration and profiles. This is a crucial skill for building deployable systems. Today, we'll tackle another fundamental concept for creating clean, modular applications: Aspect-Oriented Programming (AOP).

Imagine your application's business logic is cluttered with repetitive code for logging, security checks, or transaction management. This "noise" makes the core logic hard to read and maintain. AOP provides a powerful solution to this problem by separating these cross-cutting concerns from your business logic.

Today's learning outcome is to explain Spring AOP concepts and implement a cross-cutting concern (e.g., logging) using an aspect. Mastering AOP is essential for writing clean, enterprise-grade code and is a frequent topic in senior developer interviews.

1. What is AOP and Why Do We Need It?

Aspect-Oriented Programming (AOP) complements Object-Oriented Programming (OOP) by providing another way of thinking about the structure of a program. While OOP's primary unit of modularity is the class, AOP's is the aspect. AOP allows us to modularize concerns that cut across multiple types and objects, such as logging, performance monitoring, and security.

Let's start with a resource that provides a great analogy and introduces the core concepts you'll need to know for any interview.

AOP Deep Dive in Spring Boot: The Must-Read Guide ...

The article 'AOP Deep Dive in Spring Boot' introduces AOP using a simple theater play analogy that makes the concept very intuitive. It then defines the essential terminology.

Please read the sections 'Why AOP? A Real-World Analogy' and 'Core Concepts & Terminology'. Focus on understanding the role of AOP and the definitions of Aspect, JoinPoint, Pointcut, Advice, and Weaving. These terms are the foundation of any AOP discussion.

To summarize the key terminology:

  • Concern: A specific functionality or behavior of a system. A core concern is the business logic (e.g., processing a payment), while a cross-cutting concern is one that is spread across the application (e.g., logging every method call).
  • Aspect: A module that encapsulates a cross-cutting concern. In Spring, it's a class annotated with @Aspect.
  • Join Point: A specific point during the execution of a program, such as a method call or an exception being thrown. In Spring AOP, a join point is always the execution of a method.
  • Advice: The action taken by an aspect at a particular join point. This is the code that implements the cross-cutting concern.
  • Pointcut: An expression that selects the join points where an advice should be executed. It answers the question: where should the advice be applied?
  • Weaving: The process of applying aspects to a target object to create a new proxied object. Weaving can happen at compile time, load time, or runtime. Spring AOP performs weaving at runtime.

2. Implementing a Logging Aspect

Now, let's put theory into practice by implementing a common cross-cutting concern: logging method calls. This is a classic AOP example and a great way to demonstrate your practical skills in an interview.

Step 1: Add the AOP Dependency

First, you need to ensure the AOP starter is in your pom.xml (for Maven) or build.gradle file.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

Spring Boot will auto-configure everything needed once this dependency is present.

Step 2: Create the Aspect

An aspect is just a regular Spring bean, annotated with @Aspect and @Component.

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {

    private static final Logger LOGGER = LoggerFactory.getLogger(LoggingAspect.class);

    // We will add advice methods here
}

Step 3: Define a Pointcut and Add Advice

Next, we define where our logging logic should run using a pointcut expression, and we define what logic should run using an advice annotation.

Let's say we want to log a message right before any method in our service layer is executed. We can use the @Before advice.

@Aspect
@Component
public class LoggingAspect {

    private static final Logger LOGGER = LoggerFactory.getLogger(LoggingAspect.class);

    @Before("execution(* com.yourapp.service.*.*(..))")
    public void logBeforeMethodExecution(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().toShortString();
        LOGGER.info("===> Executing: {}", methodName);
    }
}

Let's break down the pointcut expression execution(* com.yourapp.service.*.*(..)):

  • execution(): A designator that tells Spring we're matching method execution join points.
  • *: The first wildcard matches any return type.
  • com.yourapp.service.*: Matches any class in the service package.
  • .*: Matches any method name.
  • (..): Matches any number of arguments (zero or more).

The JoinPoint object gives us metadata about the method being executed, like its signature and arguments.

Test your understanding!

You need to apply an advice to all public methods within any class in the com.myapp.repository package that returns a String. How would you write the pointcut expression?

Show answer

The pointcut expression would be:
execution(public String com.myapp.repository.*.*(..))

  • public: Matches only public methods.
  • String: Matches methods that return a String.
  • com.myapp.repository.*: Matches any class in the specified package.
  • .*(..): Matches any method name with any arguments.

3. Exploring All Advice Types

Spring AOP provides five types of advice, each serving a different purpose. Understanding them all is crucial for interviews. The @Around advice is particularly important.

Let's look at a resource that details all five types with code examples.

AOP Deep Dive in Spring Boot: The Must-Read Guide ...

The 'AOP Deep Dive' article provides clear, copy-pasteable code examples for all five advice types. It also includes a great example for @Around advice to measure method execution time.

Please read the sections 'Diving Deeper: All Advice Types' and 'Mastering Pointcut Expressions'. Pay close attention to the purpose of each advice annotation and the code example provided. The @Around advice is the most powerful and flexible, so make sure you understand how ProceedingJoinPoint is used.

Here is a summary of the five advice types:

  1. @Before: Runs before the target method.
  2. @AfterReturning: Runs after the target method completes successfully (i.e., does not throw an exception). It can access the return value.
  3. @AfterThrowing: Runs if the target method throws an exception. It can access the thrown exception.
  4. @After: Runs after the target method finishes, regardless of whether it was successful or threw an exception (like a finally block).
  5. @Around: The most powerful advice. It "surrounds" the method execution. You have full control: you can execute logic before and after the method, change the arguments, change the return value, or even prevent the method from running at all. It uses ProceedingJoinPoint.proceed() to call the original method.
Test your understanding!

You want to implement a custom caching mechanism. Your logic needs to:

  1. Check if a result is in the cache before the target method runs.
  2. If it is, return the cached result without running the method.
  3. If it's not, run the target method.
  4. After the method runs successfully, put its result into the cache.

Which advice type is best suited for this and why?

Show answer

@Around advice is the only suitable choice.

Why?

  • It allows you to execute logic before and after the target method call within a single advice method.
  • Crucially, it gives you control over whether the target method is executed at all via pjp.proceed(). For a cache hit, you would simply return the cached value and not call pjp.proceed().
  • It also allows you to capture the return value from pjp.proceed() to place it in the cache.

4. How AOP Works: The Magic of Proxies

For a senior-level interview, you'll be expected to explain how Spring AOP works under the hood. The answer lies in dynamic proxies.

When the Spring container starts, it doesn't give you back the original bean. Instead, it creates a proxy object that wraps your original bean. This proxy has the same methods as your original bean. When you call a method on the proxy, it intercepts the call, executes any matching advice (like our logging logic), and then—if appropriate—delegates the call to the original method on the target object.

Spring uses two types of proxies to achieve this:

  1. JDK Dynamic Proxies: The default for beans that implement at least one interface. The proxy is created at runtime and implements the same interfaces as the target class.
  2. CGLIB Proxies: Used for beans that do not implement an interface. This works by creating a subclass of your bean at runtime and overriding its methods to add the advice logic. In modern Spring Boot, CGLIB is often the default even for interface-based proxies.

This image provides an excellent visual summary of the two approaches.

JDK vs CGLib Proxies in Spring AOP
This diagram illustrates the two proxy mechanisms Spring AOP uses. JDK proxies are interface-based, where the proxy implements the same interface as your service. CGLIB proxies are subclass-based, where the proxy extends your service class directly. Both intercept method calls to inject advice logic.

A common interview "gotcha" related to this is the self-invocation problem. If you call an advised method from another method within the same class, the advice will not be triggered. This is because the call (this.someMethod()) bypasses the proxy and directly invokes the method on the original target object.


5. Getting Interview-Ready

You now have the core concepts and practical knowledge. Let's focus on structuring this for an interview.

Mastering Spring AOP: The Ultimate Guide for 2025

The article 'Mastering Spring AOP' has an excellent section with common interview questions and structured answers. This is perfect for consolidating your knowledge.

Read the sections 'Interview Questions and Answers on Spring AOP' and 'What are the different types of advice in Spring AOP?'. Focus on how to clearly and concisely answer questions about the core concepts, advice types, and the difference between Spring AOP and the more powerful AspectJ framework.

Key Interview Questions to Practice:

  1. What is AOP, and what problems does it solve? (Answer: It modularizes cross-cutting concerns like logging, security, and transactions, keeping business logic clean.)
  2. Can you explain the core concepts of AOP? (Answer: Aspect, Join Point, Advice, Pointcut, Weaving.)
  3. What are the five types of advice in Spring AOP? Give a use case for each. (Answer: Before, After, AfterReturning, AfterThrowing, and Around. Be ready with examples like logging, resource cleanup, or caching.)
  4. How does Spring AOP work internally? (Answer: Through runtime weaving using dynamic proxies—either JDK Dynamic Proxies or CGLIB.)
  5. What is the self-invocation problem? (Answer: Calling an advised method from within the same object bypasses the proxy, so the advice doesn't run.)

Conclusion

Today you've learned how Spring AOP uses aspects and proxies to cleanly separate cross-cutting concerns from your business logic. This not only makes your code more maintainable but also demonstrates a level of design maturity expected in senior engineering roles.

Key Takeaways:

  • AOP is used to modularize cross-cutting concerns.
  • The core components are the Aspect (the what), the Pointcut (the where), and the Advice (the how).
  • Spring AOP provides five advice types, with @Around being the most powerful.
  • The magic happens at runtime via dynamic proxies (JDK or CGLIB), which wrap your target beans to intercept method calls.

Preview of the Next Lesson:
We've just seen how Spring cleverly wraps beans with proxies to add new behavior. This highlights the complexity of how beans are created and wired together. In the next lesson, we'll dive into a common problem that arises from this wiring process: circular dependencies. You will learn how to solve a circular dependency problem in a Spring application and explain the underlying cause, a classic interview scenario that tests your deep understanding of the Spring IoC container.