Backend Development 10 min read

Master Spring AOP: Enabling @AspectJ and Building Custom Pointcuts

This article explains how to enable @AspectJ support in Spring (both via annotations and XML), defines custom AspectJ pointcuts and advice, and dives into Spring AOP APIs such as Pointcut, ClassFilter, MethodMatcher, and utility classes for composing pointcuts.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Master Spring AOP: Enabling @AspectJ and Building Custom Pointcuts

Environment: Spring 5.3.23

Overview

In daily work we most often use @Aspect to implement AOP. To use @Aspect in Spring configuration, you need to enable Spring support so that Spring AOP can create proxies automatically for beans that match one or more aspects.

You can enable @AspectJ support via XML or Java‑style configuration, but you must ensure that aspectjweaver.jar (version 1.8+) is on the classpath.

Enable @Aspect support with annotations

<code>@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
}
</code>

Enable @Aspect support with XML

<code>&lt;aop:aspectj-autoproxy/&gt;
</code>

Define an AspectJ aspect

<code>package com.pack.aspect;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class CustomAspect {
  // Define pointcut
  @Pointcut("execution(* com.pack.service..*.(..))")
  private void log() {}

  // Define advice
  @Before("log()")
  public void recordLogBefore() {
    // ...
  }

  @AfterReturning("log()")
  public void recordLogAfter() {
    // ...
  }
}
</code>

The above briefly reviews how to use @Aspect to define aspects for AOP.

Spring AOP API

Spring Pointcut API

Spring’s pointcut model allows reuse of pointcuts independent of advice type. The Pointcut interface is the central interface:

<code>public interface Pointcut {
  ClassFilter getClassFilter();
  MethodMatcher getMethodMatcher();
}
</code>

The interface is split into ClassFilter and MethodMatcher to allow fine‑grained composition.

ClassFilter

Limits a pointcut to a given set of target classes. If matches() always returns true, all classes match.

<code>public interface ClassFilter {
  boolean matches(Class clazz);
}
</code>

MethodMatcher

Determines whether a method matches at runtime.

<code>public interface MethodMatcher {
  /** Called when a method is invoked to decide if the advice should run */
  boolean matches(Method m, Class<?> targetClass);
  /** Determines whether the runtime check should be performed */
  boolean isRuntime();
  /** Runtime check with arguments */
  boolean matches(Method m, Class<?> targetClass, Object... args);
}
</code>

Spring supports pointcut operations such as union and intersection. Union matches if any of the combined pointcuts match.

<code>public abstract class Pointcuts {
  public static Pointcut union(Pointcut pc1, Pointcut pc2) {
    return new ComposablePointcut(pc1).union(pc2);
  }
}
</code>

ComposablePointcut combines class filters and method matchers.

<code>public class ComposablePointcut implements Pointcut, Serializable {
  private ClassFilter classFilter;
  private MethodMatcher methodMatcher;
  public ComposablePointcut(Pointcut pointcut) {
    this.classFilter = pointcut.getClassFilter();
    this.methodMatcher = pointcut.getMethodMatcher();
  }
  public ComposablePointcut union(Pointcut other) {
    this.methodMatcher = MethodMatchers.union(this.methodMatcher, this.classFilter,
                                               other.getMethodMatcher(), other.getClassFilter());
    this.classFilter = ClassFilters.union(this.classFilter, other.getClassFilter());
    return this;
  }
}
</code>

MethodMatchers.union creates a combined matcher.

<code>static MethodMatcher union(MethodMatcher mm1, ClassFilter cf1,
                           MethodMatcher mm2, ClassFilter cf2) {
  return (mm1 instanceof IntroductionAwareMethodMatcher || mm2 instanceof IntroductionAwareMethodMatcher)
         ? new ClassFilterAwareUnionIntroductionAwareMethodMatcher(mm1, cf1, mm2, cf2)
         : new ClassFilterAwareUnionMethodMatcher(mm1, cf1, mm2, cf2);
}
</code>

ClassFilterAwareUnionMethodMatcher evaluates matches based on the two class filters and method matchers.

<code>private static class ClassFilterAwareUnionMethodMatcher extends UnionMethodMatcher {
  private final ClassFilter cf1;
  private final ClassFilter cf2;
  public ClassFilterAwareUnionMethodMatcher(MethodMatcher mm1, ClassFilter cf1,
                                            MethodMatcher mm2, ClassFilter cf2) {
    super(mm1, mm2);
    this.cf1 = cf1;
    this.cf2 = cf2;
  }
}
private static class UnionMethodMatcher implements MethodMatcher, Serializable {
  public boolean matches(Method method, Class<?> targetClass) {
    return (matchesClass1(targetClass) && this.mm1.matches(method, targetClass)) ||
           (matchesClass2(targetClass) && this.mm2.matches(method, targetClass));
  }
}
</code>

Spring provides convenient pointcut implementations such as static pointcuts, regex pointcuts, and dynamic pointcuts.

Static pointcuts are based on method and target class and are evaluated only once per method.

Regex pointcut example:

<code>&lt;bean id="staticRegexpPoint" class="org.springframework.aop.support.JdkRegexpMethodPointcut"&gt;
  &lt;property name="patterns"&gt;
    &lt;list&gt;
      &lt;value&gt;.*set.*&lt;/value&gt;
      &lt;value&gt;.*save&lt;/value&gt;
    &lt;/list&gt;
  &lt;/property&gt;
  &lt;property name="advice" ref="logAdvice"/&gt;
&lt;/bean&gt;
</code>

Dynamic pointcuts consider method arguments and are evaluated on each call. Example core class:

<code>public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher, Serializable {
  private final Class<?> clazz;
  private final String methodName;
  public ControlFlowPointcut(Class<?> clazz, @Nullable String methodName) {
    this.clazz = clazz;
    this.methodName = methodName;
  }
  public boolean matches(Method method, Class<?> targetClass) { return true; }
  public boolean isRuntime() { return true; }
  public boolean matches(Method method, Class<?> targetClass, Object... args) {
    for (StackTraceElement element : new Throwable().getStackTrace()) {
      if (element.getClassName().equals(this.clazz.getName()) &&
          (this.methodName == null || element.getMethodName().equals(this.methodName))) {
        return true;
      }
    }
    return false;
  }
}
</code>

Spring also offers abstract base classes such as StaticMethodMatcherPointcut for creating custom static pointcuts.

<code>public class CustomStaticPointcut extends StaticMethodMatcherPointcut {
  public boolean matches(Method m, Class targetClass) {
    // return true if custom criteria match
  }
}
</code>

End of section. The next part will cover Spring AOP advice API in detail.

JavaAOPSpringAdviceAspectJPointcut
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.