Inter-Aspect Communication in Spring AOP: Passing Data Between Advice Methods

Problem Statement

In a flash sale system, I encountered a scenario where one aspect handles Redis-based distributed locking and rate limiting, while another aspect is responsible for publishing order messages to a message queue. The core challenge: how to transmit data computed in the first aspect to the second aspect for further processing.

Solution Approach

The technique involves manipulating method arguments through the ProceedingJoinPoint and leveraging excetpion handling to control execution flow.

@GetMapping("/createOrder")
public ResponseEntity<String> submitOrder(
        @RequestParam("itemId") String itemId,
        @RequestParam("verificationKey") String key,
        @RequestParam("quantity") Integer count) {
    String orderNumber = orderService.processOrder(itemId, key, count);
    if (orderNumber == null) {
        return ResponseEntity.badRequest().build();
    }
    return ResponseEntity.ok(orderNumber);
}

The key insight is to force the argument replacement during aspect execution:

try {
    return joinPoint.proceed(new Object[]{orderPayload, null, null});
} catch (Throwable ex) {
    return generatedOrderNumber;
}

Critical Constraints

Argument Count Matching: The replacement arguments must exactly match the method signature in count and position. Mismatched arguments cause the JVM to throw an IllegalArgumentException, preventing the original method execution and blocking access to subsequent aspects.

java.lang.IllegalArgumentException: Expecting 3 arguments to proceed, 
but was passed 1 arguments

Exception Handling Discipline: The caught exception must not be re-thrown. If the exception propagates, the downstream aspect receives incorrect data types, causing casting failures:

cn.miozus.gulimall.common.to.mq.SeckillOrderTo cannot be cast to java.lang.String

Return Value Behavior: Although both aspects return the same orderSn, only the value from the first aspect reaches the controller layer. The second aspect's return value becomes irrelevant in the execution chain.

Execution Flow

Setting breakpoints reveals the interception sequence:

  1. First aspect: Computes the result, prepares to invoke the second aspect
  2. Second aspect: Processes the message queue operations
  3. Original method: Returns control to the caller

Logs confirm the expected behavior:

2022-03-29 17:32:56.521  INFO 7904 --- [io-25000-exec-8] 
  c.m.g.s.aspect.SeckillRabbitMqAspect     : 快速创建订单:发送消息创建完成: 
  202203291732444881508738921192005634
2022-03-29 17:33:01.526  INFO 7904 --- [io-25000-exec-8] 
  c.m.g.s.controller.SeckillController     : 秒杀创建订单用时:28778
🎊 seckill orderSn = 202203291732444881508738921192005634
2022-03-29 17:33:01.527  INFO 7904 --- [nectionFactory5] 
  c.m.g.s.config.RabbitMqSeckillConfig     : 📨 消息已发送, 
  params: correlationData:null,ack:true,cause:null

Alternative Design

A simpler approach eliminates aspect composition entirely by injecting one service into another and invoking methods directly. While this removes the AOP decoupling benefits, it simplifies data flow for straightforward scenarios where aspect ordering isn't critical.

@Service
public class OrderProcessingService {
    
    @Autowired
    private RateLimitService rateLimitService;
    
    @Autowired
    private MessagePublisherService messagePublisher;
    
    public String executeOrderProcessing(OrderPayload payload) {
        // Direct method invocation without aspect interception
        String ticket = rateLimitService.acquireTicket(payload);
        messagePublisher.sendOrderMessage(ticket);
        return ticket;
    }
}

This approach trades aspect-oriented flexibility for direct control over execution sequence and data passing mechanisms.

Tags: Spring aop aspect-communication interceptor proceedingjoinpoint

Posted on Sun, 10 May 2026 23:46:02 +0000 by Qben