Implementing Aspect-Oriented Programming in Spring with XML and Annotation Configurations

Core Concepts of Aspect-Oriented Programming

Overview

Aspect-Oriented Programming (AOP) focuses on modularizing cross-cutting concerns. It employs either compile-time weaving or runtime dynamic proxies to apply additional behavior to existing code without altering the source files.

Purpose: To inject supplementary logic (like logging or security) into methods during runtime without touching the original codebase. Benefits: Eliminates code duplication, accelerates development, and simplifies maintenance. Mechanism: Spring utilizes dynamic proxying at runtime to generate proxy objects that wrap the original beans.

Proxying Techniques

  • JDK Dynamic Proxy: Requires the target to implement at least one interface. The proxy implements the same interfaces.
  • CGLIB Proxy: Creates a subclass of the target class. Useful when the target does not implement interfacse.

Key Terminology

Term Description
Target Object The original object being advised by one or more aspects.
Proxy The object created by the AOP framework after applying advice to the target object.
Join Point A specific point during program execution, such as method invocation. In Spring AOP, this is always a method execution.
Pointcut A predicate that matches join points. Advice is associated with a pointcut expression and runs at any join point matched by the pointcut.
Advice Action taken at a particular join point. Different types include "around", "before", and "after".
Aspect A modularization of a concern that cuts across multiple classes. It combines pointcuts and advice.
Weaving Linking aspects with other application types or objects to create an advised object. Spring performs weaving at runtime.

Development Tasks

  1. Implement the core business logic (Target methods).
  2. Define an Aspect class containing the enhancement logic (Advice methods).
  3. Configure the weave relationship (mapping advice to specific pointcuts) via XML or Annotations.

Pointcut Expression Syntax

The standard syntax for defining where advice should be applied is:

execution([Visibility] ReturnType [ClassPath].MethodName(Args))
  • Visibility can be omitted.
  • Use * as a wildcard for return type, package, class, or method names.
  • A single . matches the current package; .. matches the current package and all sub-packages.
  • .. in arguments matches any number and type of arguments.

Examples:

// Matches a specific public method
execution(public void com.example.service.Calculator.add())

// Matches any method returning void in the service package
execution(void com.example.service.*.*(..))

// Matches any method in the service package or sub-packages
execution(* com.example.service..*.*(..))

Advice Types

Advice Type XML Tag Annotation Behavior
Before <aop:before> @Before Executes logic prior to the method invocation.
After Returning <aop:after-returning> @AfterReturning Executes logic after the method completes successfully.
Around <aop:around> @Around Wraps the method, alllowing logic before and after, and capable of modifying return values or handling exceptions.
After Throwing <aop:after-throwing> @AfterThrowing Executes logic if the method throws an exception.
After (Finally) <aop:after> @After Executes logic regardless of the outcome (similar to finally).

Implementation Guide

1. XML-Based Configuration

(1) Dependencies

Add the necessary Spring and AspectJ libraries to your project.

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.8</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.8.6</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
</dependencies>

(2) Target Class

Define a simple service interface and its implementation.

package com.cqgcxy.service;

public interface PhoneService {
    void select();
}
package com.cqgcxy.service.impl;

import com.cqgcxy.service.PhoneService;

public class PhoneServiceImpl implements PhoneService {
    @Override
    public void select() {
        System.out.println("Global Market: Huawei Dominance");
    }
}

(3) Aspect Class

Create a class to handle the logging logic.

package com.cqgcxy.monitor;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import java.time.LocalDateTime;

public class PerformanceMonitor {
    public Object trackExecution(ProceedingJoinPoint pjp) throws Throwable {
        Signature sig = pjp.getSignature();
        LocalDateTime start = LocalDateTime.now();
        System.out.println(start + " -> " + sig + " -> Start");
        
        Object result = pjp.proceed();
        
        LocalDateTime end = LocalDateTime.now();
        System.out.println(end + " -> " + sig + " -> End");
        return result;
    }
}

(4) Spring Configuration (XML)

Register beans and define the AOP weaving.

<bean id="phoneService" class="com.cqgcxy.service.impl.PhoneServiceImpl" />
<bean id="monitor" class="com.cqgcxy.monitor.PerformanceMonitor" />

<aop:config>
    <aop:aspect ref="monitor">
        <!-- Option 1: Inline pointcut -->
        <aop:around 
            method="trackExecution" 
            pointcut="execution(* com.cqgcxy.service..*(..))" />
        
        <!-- Option 2: Referenced pointcut -->
        <!-- 
        <aop:pointcut id="serviceOps" expression="execution(* com.cqgcxy.service..*(..))"/>
        <aop:around method="trackExecution" pointcut-ref="serviceOps"/> 
        -->
    </aop:aspect>
</aop:config>

(5) Test Case

package com.cqgcxy.service.impl;

import com.cqgcxy.service.PhoneService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class PhoneServiceImplTest {

    @Autowired
    private PhoneService phoneService;

    @Test
    public void testSelect() {
        phoneService.select();
    }
}

2. Annotation-Based Configuration

(1) Component Scanning and Enabling AOP

Use a Java configuration class to replace the XML file.

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

(2) Annotated Service

package com.cqgcxy.service.impl;

import com.cqgcxy.service.PhoneService;
import org.springframework.stereotype.Service;

@Service
public class PhoneServiceImpl implements PhoneService {
    @Override
    public void select() {
        System.out.println("Global Market: Huawei Dominance");
    }
}

(3) Aspect with Annotations

Annotate the class with @Aspect and @Component.

package com.cqgcxy.monitor;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.time.LocalDateTime;

@Aspect
@Component
public class PerformanceMonitor {

    @Around("execution(* com.cqgcxy.service.impl.PhoneServiceImpl.select(..))")
    public Object trackExecution(ProceedingJoinPoint pjp) throws Throwable {
        LocalDateTime start = LocalDateTime.now();
        System.out.println(start + " -> " + pjp.getSignature() + " -> Start");
        
        Object result = pjp.proceed();
        
        LocalDateTime end = LocalDateTime.now();
        long millis = Duration.between(start, end).toMillis();
        System.out.println(end + " -> " + pjp.getSignature() + " -> End (" + millis + "ms)");
        return result;
    }
}

(4) Test Case

package com.cqgcxy.service.impl;

import com.cqgcxy.service.PhoneService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class PhoneServiceImplTest {

    @Autowired
    private PhoneService phoneService;

    @Test
    public void testSelect() {
        phoneService.select();
    }
}

Utility Methods in ProceedingJoinPoint

The ProceedingJoinPoint interface provides context about the intercepted method:

  • Object proceed(): Executes the next advice or the target method.
  • Object proceed(Object[] args): Executes the target method with modified arguments.
  • Signature getSignature(): Retrieves method details (name, return type, etc.).
  • Object getTarget(): Returns the actual target object instance.
  • Object getThis(): Returns the proxy object.
  • Object[] getArgs(): Returns the array of arguments passed to the method.

Tags: Spring Framework aop aspectj java Dependency Injection

Posted on Sun, 11 Oct 2026 16:34:32 +0000 by stevegg1965