Spring AOP
SkillMonitoring & opsSpring AOP (Aspect-Oriented Programming) for Spring Boot 3.x. Covers @Aspect, pointcut expressions, @Around/@Before/@After advice, custom annotations, logging, security, caching, and transaction patterns. Use for cross-cutting concerns.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Spring AOP skill
What this skill tells your AI
The instructions your AI receives, as published by claude-dev-suite/claude-dev-suite in skills/backend-frameworks/spring-aop/SKILL.md and read by ahel’s review.
Full Reference: See patterns.md for Performance Monitoring, Retry Logic, Audit Logging, Security Check, Rate Limiting, and Testing patterns.
Quick Start
@SpringBootApplication
@EnableAspectJAutoProxy
public class Application {}
@Aspect
@Component
@Slf4j
public class LoggingAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object logExecution(ProceedingJoinPoint joinPoint) throws Throwable {
String method = joinPoint.getSignature().toShortString();
log.info("Executing: {}", method);
long start = System.currentTimeMillis();
Object result = joinPoint.proceed();
long duration = System.currentTimeMillis() - start;
log.info("Completed: {} in {} ms", method, duration);
return result;
}
}
Pointcut Expressions
Method Execution
@Aspect
@Component
public class PointcutExamples {
// All methods in a package
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {}
// Public methods
@Pointcut("execution(public * *(..))")
public void publicMethods() {}
// Methods starting with "get"
@Pointcut("execution(* get*(..))")
public void getters() {}
// Package and subpackages
@Pointcut("execution(* com.example..*.*(..))")
public void entireApplication() {}
}
Annotation-Based
@Aspect
@Component
public class AnnotationPointcuts {
// Methods annotated with @Transactional
@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional)")
public void transactionalMethods() {}
// Classes annotated with @Service
@Pointcut("@within(org.springframework.stereotype.Service)")
public void serviceBeans() {}
// With annotation binding
@Around("@annotation(loggable)")
public Object logWithAnnotation(ProceedingJoinPoint pjp, Loggable loggable) throws Throwable {
String value = loggable.value();
return pjp.proceed();
}
}
Combinations
@Aspect
@Component
public class CombinedPointcuts {
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {}
@Pointcut("execution(* com.example.repository.*.*(..))")
public void repositoryLayer() {}
// AND
@Pointcut("serviceLayer() && publicMethods()")
public void publicServiceMethods() {}
// OR
@Pointcut("serviceLayer() || repositoryLayer()")
public void dataLayer() {}
// NOT
@Pointcut("serviceLayer() && !execution(* get*(..))")
public void nonGetterServiceMethods() {}
}
Advice Types
@Around
@Aspect
@Component
public class AroundAdvice {
@Around("execution(* com.example.service.*.*(..))")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
String methodName = joinPoint.getSignature().getName();
Object[] args = joinPoint.getArgs();
log.info("Before: {} with args: {}", methodName, Arrays.toString(args));
try {
Object result = joinPoint.proceed();
log.info("After returning: {} returned: {}", methodName, result);
return result;
} catch (Exception e) {
log.error("After throwing: {} threw: {}", methodName, e.getMessage());
throw e;
}
}
// Modify arguments
@Around("execution(* com.example.service.UserService.createUser(..))")
public Object sanitizeInput(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] args = joinPoint.getArgs();
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof String str) {
args[i] = sanitize(str);
}
}
return joinPoint.proceed(args);
}
}
@Before, @After, @AfterReturning, @AfterThrowing
@Aspect
@Component
public class OtherAdvice {
@Before("execution(* com.example.service.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
log.info("Entering: {}", joinPoint.getSignature().toShortString());
}
@After("execution(* com.example.service.*.*(..))")
public void logAfter(JoinPoint joinPoint) {
log.info("Completed: {}", joinPoint.getSignature().toShortString());
}
@AfterReturning(pointcut = "execution(* com.example.service.UserService.createUser(..))", returning = "user")
public void logCreatedUser(JoinPoint joinPoint, User user) {
log.info("Created user: {}", user.getId());
}
@AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "ex")
public void logException(JoinPoint joinPoint, Exception ex) {
log.error("Exception in {}: {}", joinPoint.getSignature().toShortString(), ex.getMessage());
}
}
Custom Annotations
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Loggable {
String value() default "";
boolean logArgs() default true;
boolean logResult() default true;
LogLevel level() default LogLevel.INFO;
}
@Aspect
@Component
@Slf4j
public class LoggableAspect {
@Around("@annotation(loggable)")
public Object logMethod(ProceedingJoinPoint joinPoint, Loggable loggable) throws Throwable {
String methodName = joinPoint.getSignature().toShortString();
String message = loggable.value().isEmpty() ? methodName : loggable.value();
if (loggable.logArgs()) {
log.info("Entering {} with args: {}", message, Arrays.toString(joinPoint.getArgs()));
}
long start = System.currentTimeMillis();
Object result = joinPoint.proceed();
if (loggable.logResult()) {
log.info("Exiting {} with result: {} ({}ms)", message, result, System.currentTimeMillis() - start);
}
return result;
}
}
// Usage
@Service
public class UserService {
@Loggable(value = "Creating user", logResult = false)
public User createUser(CreateUserRequest request) {
return userRepository.save(new User(request));
}
}
Aspect Ordering
@Aspect
@Component
@Order(1) // Executes first
public class SecurityAspect { }
@Aspect
@Component
@Order(2) // Executes second
public class LoggingAspect { }
@Aspect
@Component
@Order(3) // Executes third
public class PerformanceAspect { }
Best Practices
- Use specific pointcuts (not too broad)
- Order aspects with @Order
- Use custom annotations for clarity
- Avoid complex logic in aspects
- Monitor performance impact
- Test aspects in isolation
- Don't catch exceptions silently
- Don't modify shared state
- Don't use for core business logic
Common Pitfalls
| Error | Cause | Solution |
|---|---|---|
| Aspect not applied | Bean not Spring-managed | Use @Component or @Bean |
| Self-invocation | Internal call bypasses proxy | Use self-injection |
| Wrong order | Aspects not ordered | Use @Order |
| Performance | Aspect too broad | Narrow pointcut |
Quick Troubleshooting
| Issue | Possible Cause | Solution |
|---|---|---|
| Aspect not executing | Bean not Spring-managed | Ensure class has @Component |
| Self-invocation doesn't trigger | Internal call bypasses proxy | Use self-injection |
| Wrong execution order | Missing @Order | Add @Order with priority |
| Performance degradation | Pointcut too broad | Narrow scope, use custom annotations |
Reference Documentation
Signals
- GitHub stars
- 33
- Forks
- 6
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
spring-aop- Source
- github.com/claude-dev-suite/claude-dev-suite