Understanding Spring AOP Proxies
When working with Spring AOP, it's crucial to understand that only method calls through Spring-managed beans will trigger aspect advice. Direct this calls bypass the proxy mechanism entirely, meaning aspects won't intercept them. This happens because service objects are injected as Spring proxies, which are separate from objects created via this.
Pointcut Expressions
Core Expression Types
@Pointcut(value="execution(* com.example.UserService.*(..))")
The @Pointcut annotation defines reusable pointcuts using various expression types:
execution() - Matches method execution join points
args() - Matches method executions with specific parameter types
this() - Matches execution on AOP proxy objects of a given type
target() - Matches execution on target objects (not including introduced interfaces)
within() - Matches method executions within specified types
@args() - Matches executions where method arguments carry specific annotations
@target() - Matches executions on types marked with specific annotations
@within() - Matches all method executions within types carrying specific annotations
@annotation() - Matches methods annotated with a specific annotation
Execution Expression Syntax
execution(
modifier-pattern? // public, private, protected (optional)
ret-type-pattern // return type
declaring-type-pattern? // class path (optional)
name-pattern(param-pattern) // method name and parameters
throws-pattern? // exception type (optional)
)
Common Patetrns
execution(public * *(..)) // Any public method
execution(* set*(..)) // Methods starting with 'set'
execution(* com.example.service.AccountService.*(..)) // All methods in AccountService
execution(* com.example.service.*.*(..)) // All methods in the service package
execution(* com.example.service..*.*(..)) // All methods in service and subpackages
execution(* com.example.aop..*.*(..)) // All methods in aop package and subpackages
Wildcard Characters
*matches any sequence of characters..in type patterns matches any number of subpackages; in parameter patterns matches any number of parameters+matches subtypes of a given type (only valid as a suffix)
Practical Example: Method Execution Time Tracking
Enabling Annotation
package com.demo.aop;
import org.springframework.context.annotation.Import;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(PerformanceAspect.class)
public @interface EnablePerformanceMonitor {
}
Method Annoattion
package com.demo.aop;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Timed {
}
Aspect Implementation
package com.demo.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class PerformanceAspect {
/**
* Pointcut targeting methods annotated with @Timed
*/
@Pointcut("@annotation(com.demo.aop.Timed)")
public void timedMethods() {
}
@Around(value = "timedMethods()")
public Object measureExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
Signature signature = joinPoint.getSignature();
String methodName = signature.getName();
System.out.println("Executing: " + methodName);
long startTime = System.nanoTime();
Object result = joinPoint.proceed(joinPoint.getArgs());
long endTime = System.nanoTime();
long durationNanoseconds = endTime - startTime;
String formattedTime = (durationNanoseconds / 1_000_000) + " ms";
System.out.println("Duration: " + formattedTime);
return result;
}
}
Usage
Annotate any Spring bean method with @Timed to enable automatic execution time logging:
@Service
public class UserService {
@Timed
public User findById(Long id) {
// Method execution will be timed automatically
}
}
Enable the aspect by adding @EnablePerformanceMonitor to your configuration class or main application class.