Mastering Spring Boot AOP: Custom Annotations, Interceptors, and Self‑Invocation Pitfalls
This tutorial explains the core concepts of Spring AOP, shows how to integrate AOP in Spring Boot to create custom annotations and interceptors, and provides practical solutions for the common self‑invocation issue that causes AOP advice to be ignored.
Introduction
This guide shows how to create a custom annotation in Spring Boot and integrate it with Spring AOP to implement cross‑cutting concerns such as logging.
Core AOP Concepts
Aspect : a modular unit of cross‑cutting logic, declared with @Aspect on a class.
Joinpoint : a specific point during program execution (e.g., method invocation or exception handling).
Advice : the action taken at a joinpoint. Spring supports five advice types: @Before: runs before the joinpoint. @After: runs after the joinpoint (regardless of outcome). @AfterReturning: runs after a normal return. @AfterThrowing: runs when an exception is thrown. @Around: surrounds the joinpoint, allowing code before and after the method execution.
Pointcut : a predicate that selects which joinpoints an advice applies to, often expressed with annotation matching.
Target Object : the original bean that receives advice; Spring creates a proxy for it at runtime.
AOP Proxy : the proxy instance (JDK dynamic proxy or CGLIB) that delegates calls to the target while applying advice.
Weaving : the process of linking aspects to target objects, performed at compile time, load time, or runtime (Spring uses runtime weaving).
Integrating AOP in a Spring Boot Project
Add the AOP starter dependency to pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>Enable proxy creation by annotating a configuration class with @EnableAspectJAutoProxy. The annotation imports AspectJAutoProxyRegistrar, which registers the AnnotationAwareAspectJAutoProxyCreator bean.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {}Define a Custom Annotation
Example: a logging annotation that can carry an optional description.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
String value() default "";
}Define an Aspect
The aspect must be a Spring bean (e.g., annotated with @Component) and marked with @Aspect. Use @Order to control execution precedence when multiple aspects exist.
@Component
@Aspect
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SysLogAspect {
// pointcut and advice definitions go here
}Pointcut Expression
Intercept any method annotated with @SysLog:
@Pointcut("@annotation(com.example.annotation_demo.annotation.SysLog)")
public void pointCut() {}Around Advice
Record execution time and delegate to a placeholder log‑saving method:
@Around("pointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
long beginTime = System.currentTimeMillis();
Object result = point.proceed();
// TODO: implement log persistence
saveLog(point, beginTime);
return result;
}Usage Example
Apply @SysLog to a controller method:
@SysLog
@PostMapping("/add")
public String add() {
return "";
}Creating a Custom Annotation with a HandlerInterceptor
Interceptors run before controller methods, making them suitable for concerns such as duplicate‑submission protection.
Duplicate‑Submission Annotation
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RepeatSubmit {
/** Default expiration time: 5 seconds */
long seconds() default 5;
}Interceptor Implementation
@Component
public class RepeatSubmitInterceptor implements HandlerInterceptor {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod hm = (HandlerMethod) handler;
RepeatSubmit repeat = AnnotationUtils.findAnnotation(hm.getMethod(), RepeatSubmit.class);
if (repeat == null) return true;
String flag = ""; // build a unique key, e.g., IP+userId+uri+params
Boolean absent = stringRedisTemplate.opsForValue()
.setIfAbsent(flag, "", repeat.seconds(), TimeUnit.SECONDS);
if (absent != null && !absent) {
throw new RepeatSubmitException();
}
}
return true;
}
}Register the interceptor in Spring MVC configuration (e.g., by extending WebMvcConfigurer and overriding addInterceptors).
Testing the Interceptor
@RepeatSubmit
@GetMapping("/add")
public String add() {
return "";
}Self‑Invocation Causing AOP Advice Loss
When a method annotated with advice calls another method in the same class via this, the call bypasses the proxy, so the advice is not applied.
public class ArticleServiceImpl {
@SysLog
public void A() { /* ... */ }
public void B() {
this.A(); // advice on A() is ignored
}
}Solution 1 – Inject the Bean Itself
public class ArticleServiceImpl {
@Autowired
private ArticleService articleService; // self‑bean injected
@SysLog
public void A() { /* ... */ }
public void B() {
articleService.A(); // goes through proxy
}
}Solution 2 – Retrieve Bean from ApplicationContext
public class ArticleServiceImpl {
@SysLog
public void A() { /* ... */ }
public void B() {
ApplicationContextUtils.getApplicationContext()
.getBean(ArticleService.class).A();
}
}Solution 3 – Use AopContext to Access the Current Proxy
Enable proxy exposure: @EnableAspectJAutoProxy(exposeProxy = true).
public class ArticleServiceImpl {
@SysLog
public void A() { /* ... */ }
public void B() {
((ArticleService) AopContext.currentProxy()).A();
}
}Conclusion
The article covered fundamental AOP terminology, demonstrated how to integrate Spring AOP with a custom annotation, showed an alternative interceptor‑based approach, and provided three concrete techniques to avoid self‑invocation loss of AOP advice.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
