• Labs icon Lab
  • Core Tech
Labs

Guided: Spring Certified Professional - Aspect-oriented Programming (AOP) in Spring

This code lab will teach you how to leverage Spring Aspect-Oriented Programming (AOP) to cleanly address cross-cutting concerns like logging, validation, and performance monitoring. You'll gain practical experience implementing aspects, pointcuts, and many advice types using Spring annotations. By the end of this lab, you'll have the skills to build more modular and maintainable Spring applications by effectively separating cross-cutting concerns from core business functionality.

Labs

Path Info

Level
Clock icon Intermediate
Duration
Clock icon 44m
Published
Clock icon May 06, 2025

Contact sales

By filling out this form and clicking submit, you acknowledge our privacy policy.

Table of Contents

  1. Challenge

    Introduction

    Welcome to the lab Guided: Spring Certified Professional - Aspect-oriented Programming (AOP) in Spring.

    In this lab, you will learn how to use Spring AOP to address cross-cutting concerns in your applications. Cross-cutting concerns are functionalities like logging, security, transaction management, and monitoring that affect multiple parts of an application but are not part of the core business logic.

    AOP allows you to modularize these concerns into separate units called Aspects. This helps keep your main business logic clean and focused, making the code easier to maintain and understand.

    You will work with a simple restaurant order management system. The initial codebase includes basic services for placing orders and managing inventory and notifications. Throughout this lab, you will enhance this application by implementing various AOP aspects without modifying the core service classes. ---

    Familiarizing with the Program Structure

    The application includes the following classes in the src/main/java directory:

    • Main: This is the entry point of the application. It simulates placing and updating orders using the OrderService.
    • AppConfig: This Spring configuration class enables AOP and defines the necessary beans for services and aspects.
    • com.restaurant.service: This package contains the core business logic services:
      • OrderService: Handles placing, updating, and cancelling orders. You will target methods in this class with your aspects.
      • InventoryService: Manages item inventory.
      • NotificationService: Sends notifications (e.g., to the kitchen or manager).
    • com.restaurant.model: This package contains the data model classes like Order, MenuItem, and User.
    • com.restaurant.aspect: This is where you will spend most of your time. This package contains the Aspect classes you'll modify:
      • OrderLoggingAspect
      • OrderNotificationAspect
      • InventoryAspect
      • ValidationAspect
      • ComplexPointcutAspect
      • PerformanceMonitoringAspect

    The goal is to implement logging, notifications, inventory updates, validation, and performance monitoring using AOP concepts like Pointcut, Advice (Before, After, AfterReturning, AfterThrowing, Around), and JoinPoint.

    You can compile and run the application using the Run button. Initially, the application will compile but won't work correctly until you complete the configuration.

    Begin by examining the code to understand the program's structure. Once you're ready, start implementing the aspects step by step.

    info> If you need help, a solution directory is available for reference, containing subdirectories for each step with solution files following the naming convention [step]-[task]-[file].java (e.g., 2-1-OrderLoggingAspect.java in the step2 directory).

  2. Challenge

    Understanding AOP and Creating Your First Aspect

    Aspect-Oriented Programming (AOP) helps you separate cross-cutting concerns from your main business logic. In Spring AOP, the core concepts are aspects, pointcuts, and advices.

    Aspects

    An aspect is a module that encapsulates a specific cross-cutting concern. For example, you can have an aspect dedicated just for logging and another one just for security checks. In Spring, you define an aspect by annotating a class with @Aspect. Since aspects often need to be managed by the Spring container, you usually also annotate them with @Component or define them as beans in your configuration:

    import org.aspectj.lang.annotation.Aspect;
    import org.springframework.stereotype.Component;
    
    @Aspect
    @Component
    public class LoggingAspect {
      // Advice and Pointcuts go here
    }
    

    Pointcuts

    A pointcut is an expression that selects specific points in the execution of your application, called join points. A join point could be a method call, method execution, or even field access. Pointcuts determine where an aspect's logic should be applied.

    You define a pointcut using the @Pointcut annotation on a method within your aspect class. The annotation value contains an expression language (AspectJ pointcut expression language) to specify the target join points. For instance, you can target all methods within a specific service class or methods with a particular name pattern:

    import org.aspectj.lang.annotation.Pointcut;
    
    // Inside the @Aspect class
    @Pointcut("execution(* com.example.service.MyService.*(..))")
    public void serviceMethods() {}
    

    The above example defines a pointcut named serviceMethods. This method is meant to be empty. Spring never invokes the body of a @Pointcut method, it only reads the annotation's value, which in this case, targets the execution (execution(...)) of any method (*) in the com.example.service.MyService class, regardless of its parameters ((..)).

    Advice

    An advice is the action taken by an aspect at a particular join point selected by a pointcut. Spring AOP supports several types of advice:

    • @Before: Runs before the join point method executes.
    • @After: Runs after the join point method completes, regardless of whether it finished normally or threw an exception.
    • @AfterReturning: Runs after the join point method completes successfully (returns a value).
    • @AfterThrowing: Runs after the join point method throws an exception.
    • @Around: Wraps around the join point method execution, allowing actions before and after, and even controlling whether the original method proceeds.

    @Before Advice and JoinPoint

    The @Before advice is useful for tasks like logging entry into a method or performing pre-checks. You associate the advice with a pointcut:

    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Before;
    
    // Inside the @Aspect class
    @Before("serviceMethods()") // Reference the pointcut
    public void logBeforeServiceCall(JoinPoint joinPoint) {
      String methodName = joinPoint.getSignature().getName();
      System.out.println("Entering method: " + methodName);
      // You can access method arguments via joinPoint.getArgs()
    }
    

    The JoinPoint parameter provides context about the intercepted method execution, such as the method signature and arguments.

    Now, you'll apply these concepts to add logging to the restaurant order service.

  3. Challenge

    Working with Different Advice Types

    In addition to running code before a method executes with @Before, Spring AOP provides advice types that allow you to react after a method has finished, based on how it finished.

    The @After Advice

    The @After advice runs after a matched method execution completes, whether it returned normally or threw an exception. This is useful for cleanup tasks or actions that must happen regardless of the method's success, like releasing resources or sending final notifications.

    import org.aspectj.lang.annotation.After;
    
    // Inside the @Aspect class
    @After("serviceMethods()") // Reference a pointcut
    public void cleanupAfterServiceCall(JoinPoint joinPoint) {
      System.out.println("Method finished: " + joinPoint.getSignature().getName());
      // This runs even if the method threw an exception
    }
    

    The @AfterReturning Advice

    The @AfterReturning advice runs only when a matched method executes successfully and returns a value. You can even access the return value within the advice.

    import org.aspectj.lang.annotation.AfterReturning;
    
    // Inside the @Aspect class
    @AfterReturning(pointcut = "serviceMethods()", returning = "result")
    public void logSuccessfulReturn(JoinPoint joinPoint, Object result) {
      System.out.println("Method executed successfully: " + joinPoint.getSignature().getName());
      System.out.println("Returned value: " + result);
      // This runs only if the method did NOT throw an exception
    }
    

    Note the returning = "result" attribute. This binds the return value of the matched method to the parameter named result in the advice method. The parameter type should match (or be a supertype of) the expected return type.

    The @AfterThrowing Advice

    The @AfterThrowing advice runs only when a matched method execution throws an exception. This is ideal for logging errors, sending failure notifications, or attempting recovery actions.

    import org.aspectj.lang.annotation.AfterThrowing;
    
    // Inside the @Aspect class
    @AfterThrowing(pointcut = "serviceMethods()", throwing = "ex")
    public void handleServiceException(JoinPoint joinPoint, Throwable ex) {
      System.err.println("Exception in method: " + joinPoint.getSignature().getName());
      System.err.println("Exception message: " + ex.getMessage());
      // This runs only if the method threw an exception
    }
    

    Similar to @AfterReturning, the throwing = "ex" attribute binds the thrown exception object to the parameter named ex in the advice method. The parameter type should match (or be a supertype of) the expected exception type.

    Finally, any of the advice annotations (@Before, @After, @AfterReturning, etc.) can take either:

    1. A reference to a named @Pointcut method, or
    2. A literal pointcut expression string.

    So instead of:

    @After("serviceMethods()") // refers to a @Pointcut("...") method
    

    You can write, for example:

    @After("execution(* com.example.service.*.*(..))")
    

    Where "execution(* com.example.service.*.*(..))" is the pointcut expression itself.

    Next, you'll implement different advice types to add notification, inventory update, and error handling logic to the order service.

  4. Challenge

    Targeting with Pointcuts and Conditional Logic

    As you become more familiar with AOP, you'll need more sophisticated pointcut expressions to precisely target the join points where your advice should apply. Spring AOP, using the AspectJ pointcut language, offers powerful ways to achieve this.

    For example, here are two types of advanced pointcut expressions:

    1. Matching by Parameter Types: You can define pointcuts that match methods based on their parameter types. This is useful when you want to apply advice only to methods that handle specific kinds of data:

      // Matches any method in MyService that takes a List as its first parameter
      @Pointcut("execution(* com.example.service.MyService.*(java.util.List,..))")
      public void methodsTakingListFirst() {}
      

      Here, java.util.List specifies the type of the first parameter, and .. indicates that there might be zero or more subsequent parameters of any type.

    2. Combining Pointcuts with Logical Operators: You can combine existing pointcut definitions using logical operators like && (AND), || (OR), and ! (NOT). This allows for complex targeting logic without duplicating expression strings:

      @Pointcut("execution(* com.example.service.ServiceA.*(..))")
      public void serviceAMethods() {}
      
      @Pointcut("execution(* com.example.service.ServiceB.*(..))")
      public void serviceBMethods() {}
      
      // Combine using OR
      @Pointcut("serviceAMethods() || serviceBMethods()")
      public void serviceAOrBMethods() {}
      
      // Apply advice to the combined pointcut
      @Before("serviceAOrBMethods()")
      public void beforeAOrB(JoinPoint joinPoint) {
        System.out.println("Executing before Service A or Service B method");
      }
      

    Using AOP for Validation

    A common use case for @Before advice combined with parameter-matching pointcuts is input validation. You can create an aspect that intercepts method calls taking certain data types (like an Order object) and validates them before the core business logic executes. If validation fails, the advice can throw an exception, preventing the actual method from running with invalid data.

    // Inside a ValidationAspect
    @Pointcut("execution(* com.example.service.*.*(com.example.model.DataObject,..))")
    public void methodsTakingDataObject() {}
    
    @Before("methodsTakingDataObject()")
    public void validateDataObject(JoinPoint joinPoint) {
      Object[] args = joinPoint.getArgs();
      DataObject data = (DataObject) args[0]; // Assuming first arg is DataObject
      if (data == null || !data.isValid()) { // Example validation logic
        throw new IllegalArgumentException("Invalid DataObject provided!");
      }
      System.out.println("DataObject validation passed.");
    }
    

    In the following tasks, you will practice creating a pointcut based on parameter types for validation and combining pointcuts using the OR operator.

  5. Challenge

    Controlling Execution with Around Advice

    The most powerful type of advice in Spring AOP is the @Around advice. Unlike other advice types that run strictly before or after a method, @Around advice wraps the execution of the matched method (the join point). This gives you complete control over the method call.

    The @Around Advice

    When you use @Around advice, your advice method essentially intercepts the call to the target method. Inside the advice method, you decide whether, when, and how the original target method should be executed. For example, you can:

    1. Choose to execute the original method, skip it entirely, or execute it multiple times.
    2. Inspect and modify the arguments passed to the original method.
    3. Inspect and modify the value returned by the original method.
    4. Perform actions both before and after the original method executes within the same advice method.

    Using ProceedingJoinPoint

    To manage the execution of the wrapped method, @Around advice methods must have a parameter of type ProceedingJoinPoint. This interface extends JoinPoint and adds the proceed method. You must call this method if you want the original target method to run. There are two versions:

    • Object proceed() throws Throwable: Executes the next advice or the target method invocation.
    • Object proceed(Object[] args) throws Throwable: Executes the next advice or the target method invocation with modified arguments.

    The return value of your @Around advice method will become the return value seen by the original caller, unless you choose to return something different or the proceed() call throws an exception.

    @Aspect
    @Component
    public class LoggingAspect {
    
      @Around("execution(* com.example.service.*.*(..))")
      public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        // logic before
        System.out.println(">>> Entering " + joinPoint.getSignature());
    
        Object result = joinPoint.proceed();
    
        // logic after
        System.out.println("<<< Exiting  " + joinPoint.getSignature());
    
        return result;
      }
    }
    
    

    In this example, the advice prints a message before calling joinPoint.proceed(), lets the original method run, prints another message, and then returns the original method's result.

    In the next tasks, you will use @Around advice to implement performance monitoring and add conditional logic for priority orders.

  6. Challenge

    Conclusion

    Congratulations on successfully completing this Code Lab!

    You can compile and run the application either by clicking the Run button in the bottom-right corner of the screen or by using the Terminal with the following commands:

    1. Compile and package the application:

      mvn clean package
      
    2. Run the application:

      java -jar target/restaurant-order-system-1.0-SNAPSHOT-jar-with-dependencies.jar
      

    When running the application, observe the output carefully. You should see how the AOP advices interleave with the core OrderService logic:

    • Logging (@Before) appears before the "Order placed" message from the service.
    • Performance (@Around) wraps the execution, printing the priority order message before the service logic (for order #2) and the timing message after.
    • Inventory Update (@AfterReturning) logs only after a successful placeOrder call.
    • Kitchen Notification (@After) logs after every placeOrder call finishes, even the one that fails.
    • Validation (@Before) throws an exception for the invalid order (order #3), preventing the core placeOrder logic from running for it.
    • Error Handling (@AfterThrowing) catches the validation exception, logs the error, and triggers the manager notification.
    • Combined Pointcut (@Before) logs before the updateOrderStatus call.

    Notice how these cross-cutting concerns are handled without modifying the OrderService code itself, demonstrating the power of AOP. ---

    Extending the Program

    Here are some ideas to further enhance your skills and extend the application's capabilities:

    1. Role-Based Security Aspect: Create a new aspect that intercepts calls to sensitive methods (e.g., cancelOrder or maybe a future applyDiscount method). Use @Before or @Around advice to check the User object associated with the operation (you might need to pass the User object as an argument or retrieve it differently). If the user's role (from user.getRole()) doesn't allow the action (e.g., only "manager" can cancel), throw a custom SecurityException. This demonstrates using AOP for security checks.

    2. Custom Annotation for Auditing: Define a custom Java annotation, for example, @Auditable. Apply this annotation to specific methods in OrderService (e.g., placeOrder, cancelOrder). Create a new aspect with a pointcut targeting methods annotated with @Auditable. Implement an advice (e.g., @AfterReturning) to log detailed audit information (like who performed the action, when, and on which order ID) whenever an annotated method completes successfully. This will teach you how to use custom annotations with AOP.

    3. Retry Logic Aspect: Implement an aspect using @Around advice that targets operations that can potentially fail. If joinPoint.proceed() throws a specific type of exception (e.g., TemporaryResourceException), the advice could catch it, wait briefly, and retry the operation a limited number of times before finally giving up and re-throwing the exception. This shows AOP for resilience patterns.

    By implementing these enhancements, you'll gain a deeper understanding of applying AOP to diverse cross-cutting concerns and controlling aspect behavior in Spring. ---

    Related Courses in Pluralsight's Library

    If you'd like to continue building your Spring skills or explore related topics, check out the courses available on this path:

    These courses cover a wide range of topics. Explore them to further your learning journey in Spring!

Esteban Herrera has more than twelve years of experience in the software development industry. Having worked in many roles and projects, he has found his passion in programming with Java and JavaScript. Nowadays, he spends all his time learning new things, writing articles, teaching programming, and enjoying his kids.

What's a lab?

Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.

Provided environment for hands-on practice

We will provide the credentials and environment necessary for you to practice right within your browser.

Guided walkthrough

Follow along with the author’s guided walkthrough and build something new in your provided environment!

Did you know?

On average, you retain 75% more of your learning if you get time for practice.