Spring AOP Internals: From Core Interfaces to Proxy Creation and Interceptor Chains
This article provides a detailed, source‑code‑level walkthrough of Spring AOP, explaining the key interfaces such as Advice, Pointcut and Advisor, how proxies are generated with JDK dynamic proxies or CGLIB, and how the interceptor chain is built and executed to apply cross‑cutting concerns.
Spring AOP is a powerful framework for implementing cross‑cutting concerns in Java applications. This article analyzes its source code to reveal how the core concepts—Advice, Pointcut, Advisor—are defined, how proxy objects are created using either JDK dynamic proxies or CGLIB, and how the interceptor chain executes the configured advice.
1. Core Interfaces
The most important interfaces in Spring AOP are Advice , Pointcut , and Advisor . They are defined in the org.springframework.aop package.
1.1 Advice
An Advice represents a specific action to be taken at a join point. Spring provides several sub‑interfaces, such as BeforeAdvice, AfterAdvice, and ThrowsAdvice. Below is a snippet of the MethodBeforeAdvice interface:
public interface MethodBeforeAdvice {
/**
* Called before the target method execution.
*/
void before(Method method, Object[] args, Object target) throws Throwable;
}Similarly, AfterReturningAdvice is defined as:
public interface AfterReturningAdvice {
/**
* Called after the target method returns successfully.
*/
void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable;
}1.2 Pointcut
A Pointcut defines which methods should be intercepted. It typically uses regular expressions or method name patterns. Two common implementations are JdkRegexpMethodPointcut and NameMatchMethodPointcut:
private Pattern[] compiledPatterns = new Pattern[0];
protected boolean matches(String pattern, int patternIndex) {
Matcher matcher = this.compiledPatterns[patternIndex].matcher(pattern);
return matcher.matches();
}
public boolean matches(String methodName) {
return methodName.equals(mappedName) || matches(methodName, mappedName);
}1.3 Advisor
An Advisor combines a Pointcut with an Advice. The concrete class DefaultPointcutAdvisor holds both references:
public class DefaultPointcutAdvisor implements Advisor, Serializable {
private Pointcut pointcut = Pointcut.TRUE;
private Advice advice;
// getters and setters omitted for brevity
@Override
public String toString() {
return getClass().getName() + ": advice [" + getAdvice() + "]";
}
}2. Spring AOP Design and Implementation
Spring AOP creates proxy objects for target beans. Depending on whether the target class implements an interface, it uses either JDK dynamic proxies or CGLIB subclassing.
2.1 ProxyFactoryBean
The ProxyFactoryBean is the entry point for creating AOP proxies. It holds a TargetSource (the actual bean) and a list of Advisor objects. When getObject() is called, it initializes the advisor chain and then creates either a singleton or prototype proxy:
public Object getObject() throws BeansException {
initializeAdvisorChain();
if (isSingleton()) {
return getSingletonInstance();
} else {
if (this.targetName == null) {
logger.warn("Using non‑singleton proxies with singleton targets is often undesirable. Enable prototype proxies by setting the 'targetName' property.");
}
return newPrototypeInstance();
}
}The initializeAdvisorChain() method resolves advisor bean names from the IoC container and adds them to the internal advisor list.
2.2 Generating the AOP Proxy
The actual proxy creation is delegated to ProxyCreatorSupport, which holds an AopProxyFactory. By default it uses DefaultAopProxyFactory:
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
return getAopProxyFactory().createAopProxy(this);
}The factory decides which implementation to use:
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface()) {
return new JdkDynamicAopProxy(config);
}
return new CglibAopProxy(config);
}
return new JdkDynamicAopProxy(config);
}2.3 JDK Dynamic Proxy
The JdkDynamicAopProxy implements both AopProxy and InvocationHandler. Its invoke() method builds a ReflectiveMethodInvocation and runs the interceptor chain:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// handle Object methods like equals and hashCode
// obtain target object from TargetSource
// retrieve interceptor chain for the method
if (chain.isEmpty()) {
return AopUtils.invokeJoinpointUsingReflection(target, method, args);
} else {
MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
return invocation.proceed();
}
}2.4 CGLIB Proxy
The CglibAopProxy creates a subclass of the target class using the CGLIB Enhancer. Its intercept() method mirrors the JDK version but works with CGLIB callbacks:
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// similar logic to JdkDynamicAopProxy.invoke()
// builds a CglibMethodInvocation and proceeds through the interceptor chain
return retVal;
}3. AOP Interceptor Invocation
Both proxy implementations ultimately delegate to ReflectiveMethodInvocation.proceed(). This method walks the list of interceptors, invoking each one in order. If an interceptor implements InterceptorAndDynamicMethodMatcher, it checks the runtime method matcher before proceeding.
public Object proceed() throws Throwable {
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
return proceed();
} else {
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}The interceptor chain is built by DefaultAdvisorChainFactory. It iterates over all configured Advisor objects, obtains the corresponding MethodInterceptor instances from the AdvisorAdapterRegistry, and adds them to the chain, handling runtime method matching when necessary.
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, Class<?> targetClass) {
List<Object> interceptorList = new ArrayList<>(config.getAdvisors().length);
boolean hasIntroductions = hasMatchingIntroductions(config, targetClass);
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
for (Advisor advisor : config.getAdvisors()) {
if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pa = (PointcutAdvisor) advisor;
if (config.isPreFiltered() || pa.getPointcut().getClassFilter().matches(targetClass)) {
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
MethodMatcher mm = pa.getPointcut().getMethodMatcher();
if (MethodMatchers.matches(mm, method, targetClass, hasIntroductions)) {
if (mm.isRuntime()) {
for (MethodInterceptor interceptor : interceptors) {
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
} else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
} else if (advisor instanceof IntroductionAdvisor) {
// similar handling for introductions
} else {
// generic advisor handling
}
}
return interceptorList;
}The AdvisorAdapterRegistry holds a list of AdvisorAdapter implementations that know how to turn a specific Advice type into a MethodInterceptor. For example, MethodBeforeAdviceAdapter converts MethodBeforeAdvice into a MethodBeforeAdviceInterceptor:
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
public boolean supportsAdvice(Advice advice) {
return advice instanceof MethodBeforeAdvice;
}
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}The interceptor itself invokes the advice before proceeding with the original method:
public Object invoke(MethodInvocation mi) throws Throwable {
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
return mi.proceed();
}Conclusion
Spring AOP’s core mechanism relies on converting user‑defined advice into MethodInterceptor objects, building an ordered interceptor chain, and executing that chain via proxy objects created with either JDK dynamic proxies or CGLIB. Understanding these internals helps developers grasp how transaction management, security, and other cross‑cutting concerns are woven into Spring applications.
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.
Sanyou's Java Diary
Passionate about technology, though not great at solving problems; eager to share, never tire of learning!
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.
