In distributed system development, recurring cross-cutting concerns (such as logging, monitoring, security, etc.) often become a major source of code redundancy. This article shares five core techniques for using Spring AOP in a Spring Boot distributed architecture, based on real project experience, to help you improve code quality and development efficiency.
1. Distributed Log Tracking: Say Goodbye to Fragmented Logs
Pain Point Analysis
In a microservices architecture, a single request may span multiple service nodes, and traditional logging methods can lead to:
- Key business logs scattered across various services
- Inability to quickly associate the complete call chain of the same request
- Lack of unified request context information
AOP Solution
@Aspect
@Component
public class DistributedLogAspect {
// Use MDC for cross-service log tracking
private static final String TRACE_ID = "X-Trace-Id";
@Pointcut("@annotation(com.example.annotation.GlobalLog)")
public void logPointcut() {}
@Around("logPointcut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
String traceId = UUID.randomUUID().toString();
MDC.put(TRACE_ID, traceId);
try {
log.info("Start processing [{}]", joinPoint.getSignature());
Object result = joinPoint.proceed();
log.info("Completed [{}]", joinPoint.getSignature());
return result;
} finally {
MDC.remove(TRACE_ID);
}
}
}
Usage Tips
- Use the
<span>@GlobalLog</span>
custom annotation to mark methods that need tracking - Use MDC (Mapped Diagnostic Context) for cross-thread log association
- Integrate Sleuth for Zipkin distributed tracing (dependencies required)
2. Interface Performance Monitoring: Identify Slow Queries in Seconds
Scenario Pain Points
In production environments, the following issues often arise:
- Difficulty in pinpointing fluctuations in interface response times
- Lack of effective monitoring for slow database queries
- Performance black holes in third-party service calls
Monitoring Aspect Implementation
@Aspect
@Component
public class PerformanceMonitorAspect {
@Autowired
private MetricsRecorder metricsRecorder;
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {}
@Around("serviceLayer()")
public Object monitorPerformance(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
try {
return pjp.proceed();
} finally {
long duration = System.currentTimeMillis() - start;
String methodName = pjp.getSignature().getName();
metricsRecorder.record(methodName, duration);
if (duration > 1000) {
log.warn("Slow method detected: {} took {}ms", methodName, duration);
}
}
}
}
Practical Suggestions
- Integrate Prometheus + Grafana for visual monitoring
- Set different thresholds based on service types (e.g., HTTP services vs. DAO operations)
- Combine with Hystrix to implement a circuit breaker mechanism
3. Distributed Lock Aspect: Elegantly Solve Concurrency Issues
Typical Scenarios
- Prevent duplicate submissions for order payments
- Inventory deduction operations
- Prevent multiple instances from executing scheduled tasks
Distributed Lock Aspect Implementation
@Aspect
@Component
public class DistributedLockAspect {
@Autowired
private RedissonClient redissonClient;
@Around("@annotation(distributedLock)")
public Object applyLock(ProceedingJoinPoint pjp, DistributedLock distributedLock) throws Throwable {
String lockKey = generateLockKey(pjp, distributedLock);
RLock lock = redissonClient.getLock(lockKey);
try {
if (lock.tryLock(distributedLock.waitTime(), distributedLock.leaseTime(), TimeUnit.SECONDS)) {
return pjp.proceed();
} else {
throw new BusinessException("Operation too frequent, please try again later");
}
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
Key Configuration
# Redisson Configuration
spring.redis.address=redis://127.0.0.1:6379
spring.redis.connection-pool-size=10
4. Service Circuit Breaker: Build a Resilient System
Circuit Breaker Aspect Implementation
@Aspect
@Component
public class CircuitBreakerAspect {
private final CircuitBreakerRegistry registry = CircuitBreakerRegistry.ofDefaults();
@Around("@annotation(circuitBreaker)")
public Object protect(ProceedingJoinPoint pjp, CircuitBreaker circuitBreaker) throws Throwable {
io.github.resilience4j.circuitbreaker.CircuitBreaker cb = registry.circuitBreaker("serviceA");
return cb.executeSupplier(() -> {
try {
return pjp.proceed();
} catch (Throwable t) {
throw new RuntimeException(t);
}
});
}
}
Circuit Breaker Strategy Configuration
resilience4j:
circuitbreaker:
configs:
default:
failureRateThreshold: 50
minimumNumberOfCalls: 10
slidingWindowType: TIME_BASED
slidingWindowSize: 10s
5. Idempotency Control: Prevent Duplicate Requests
Idempotent Aspect Implementation
@Aspect
@Component
public class IdempotentAspect {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Around("@annotation(idempotent)")
public Object checkIdempotent(ProceedingJoinPoint pjp, Idempotent idempotent) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String token = request.getHeader("X-Idempotent-Token");
if (StringUtils.isEmpty(token)) {
throw new BusinessException("Missing idempotent token");
}
Boolean result = redisTemplate.opsForValue().setIfAbsent(token, "1", 5, TimeUnit.MINUTES);
if (Boolean.TRUE.equals(result)) {
return pjp.proceed();
} else {
throw new BusinessException("Please do not submit repeatedly");
}
}
}
Pitfall Guide: AOP Usage Considerations
-
Proxy Invalidity Issues:
- Internal method calls will not trigger AOP
- Use AopContext.currentProxy() to obtain the proxy object
Execution Order Control:
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
Exception Handling Strategy:
- Handle specific exceptions in @AfterThrowing
- Avoid swallowing the original exception stack
Performance Optimization Suggestions:
- Reduce IO operations within aspects
- Use conditional pointcut expressions
Conclusion
By effectively utilizing Spring AOP, we can uniformly handle common concerns in distributed systems, achieving:
✅ Reduced code duplication (average reduction of 40% redundant code)✅ Improved system maintainability✅ Enhanced system stability✅ Rapid iteration of non-functional requirements