Implementing Aspect-Oriented Programming with Spring AOP and AspectJ

Fundamentals of Aspect-Oriented Programming

Core Concepts

AOP (Aspect Oriented Programming) is a programming paradigm focused on modularizing cross-cutting concerns. While OOP structures code around classes and objects, AOP concentrates on separating common functionalities—such as logging, security, or transaction management—from the core business logic.

Key terminology:

  • Joinpoint: A specific point in the execution of a program, typically a method invocation.
  • Pointcut: An expression that matches one or more joinpoints where advice should be applied.
  • Advice: The action taken at a particular joinpoint (e.g., before, after, or around a method).
  • Aspect: The modularization of a concern, combining pointcuts and advice.
  • Target Object: The object being advised by one or more aspects.
  • Weaving: The process of linking aspects with the target object to create an advised object.
  • Proxy: An object created by the AOP framework to implement the aspect contracts.

Development Workflow

  1. Implementation: Develop the main business logic.
  2. Extraction: Identify and extract common functionalities in to seperate advice methods.
  3. Configuration: Define pointcuts to match business methods and bind them to advice using aspects.
  4. Execution: The Spring container monitors method executions and uses proxies to apply the advice dynamically.

Getting Started with XML Configuration

Dependencies

Add the AspectJ weaver to your project:

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>

Basic Setup

  1. Create a class containing your cross-cutting logic (e.g., logging).
  2. Declare this class as a Spring bean.
  3. Configure the AOP relationships in your XML context file.
<!-- Enable AOP -->
<aop:config>
    <!-- Define a pointcut matching all methods in service layer -->
    <aop:pointcut id="serviceLayer" expression="execution(* com.example.service.*.*(..))"/>
    
    <!-- Define the aspect -->
    <aop:aspect ref="loggingAspect">
        <aop:before method="logStart" pointcut-ref="serviceLayer"/>
    </aop:aspect>
</aop:config>

Pointcut Expressions

Expressions define where the advice should be applied. They follow the pattern: execution( modifiers? return-type declaring-type? method-name(param) throws? )

Wildcards

  • *: Matches a single token (e.g., return type, method name).
  • ..: Matches zero or more parameters or packages.
  • +: Matches a specific class and its subclasses.

Examples

  • execution(public * *(..)): All public methods.
  • execution(* com.example.service.*Service.*(..)): All methods in classes ending with 'Service' in a specific package.
  • execution(* com..*.find*(..)): All find methods in any class under the com package hierarchy.

Logical Operators

Combine expressions using && (and), || (or), and ! (not).

Advice Types

1. Before Advice

Runs before the target method. If it throws an exception, the target method will not execute.

<aop:before method="validateArgs" pointcut="execution(* *..*(..))"/>

2. After Advice (Finally)

Runs after the target method, regardless of whether it threw an exception.

<aop:after method="cleanup" pointcut-ref="pt"/>

3. After Returning Advice

Runs only if the target method completes successfully.

<aop:after-returning method="processResult" pointcut-ref="pt" returning="result"/>

4. After Throwing Advice

Runs only if the target method throws an exception.

<aop:after-throwing method="handleError" pointcut-ref="pt" throwing="ex"/>

5. Around Advice

The most powerful advice. It wraps the method execution and can control whether the method actually runs.

public Object interceptOperation(ProceedingJoinPoint pjp) throws Throwable {
    System.out.println("Before method: " + pjp.getSignature().getName());
    Object output = pjp.proceed(); // Execute the actual method
    System.out.println("After method");
    return output;
}

Annotation-Based Configuration

Enabling Annotasions

Use @EnableAspectJAutoProxy in your Java configuration class:

@Configuration
@ComponentScan("com.example")
@EnableAspectJAutoProxy
public class AppConfig { }

Defining Aspects

@Aspect
@Component
public class MonitoringAspect {

    // Define a reusable pointcut
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceLayer() {}

    @Before("serviceLayer()")
    public void logAccess(JoinPoint jp) {
        System.out.println("Accessing: " + jp.getSignature().toShortString());
    }

    @Around("serviceLayer()")
    public Object measureTime(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        Object val = pjp.proceed();
        long end = System.currentTimeMillis();
        System.out.println(pjp.getSignature() + " executed in " + (end - start) + "ms");
        return val;
    }
}

Proxy Mechanisms

Spring AOP uses proxies to apply aspects.

JDK Dynamic Proxy

  • Requirement: The target object must implement at least one interface.
  • Mechanism: Creates a proxy that implements the same interfaces as the target.
  • Limitation: Cannot proxy methods not defined in an interface.
// Conceptual example of JDK Proxy
InvocationHandler handler = (proxy, method, args) -> {
    System.out.println("Proxying: " + method.getName());
    return method.invoke(targetObject, args);
};
MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
    loader, new Class[]{MyInterface.class}, handler);

CGLIB Proxy

  • Requirement: No interface required.
  • Mechanism: Generates a subclass of the target class at runtime.
  • Limitation: Cannot proxy final classes or methods.
// Conceptual example of CGLIB
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TargetClass.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
    System.out.println("Intercepted: " + method.getName());
    return proxy.invokeSuper(obj, args);
});
TargetClass proxy = (TargetClass) enhancer.create();

Switching Proxy Type

By default, Spring uses JDK proxies if interfaces exist, otherwise CGLIB. Force CGLIB usage:

  • XML: <aop:config proxy-target-class="true">
  • Annotation: @EnableAspectJAutoProxy(proxyTargetClass = true)

Practical Example: Performance Monitoring

Monitor the execution time of service layer methods.

@Aspect
@Component
public class PerformanceMonitor {

    @Around("execution(* com.example.service.*.*(..))")
    public Object trackPerformance(ProceedingJoinPoint pjp) throws Throwable {
        String className = pjp.getSignature().getDeclaringTypeName();
        String methodName = pjp.getSignature().getName();
        
        long startTime = System.nanoTime();
        try {
            return pjp.proceed();
        } finally {
            long duration = System.nanoTime() - startTime;
            System.out.printf("%s.%s executed in %.2f ms%n", className, methodName, duration / 1e6);
        }
    }
}

Tags: Spring aop aspectj java Proxy Pattern

Posted on Wed, 22 Jul 2026 17:04:00 +0000 by terry2