Unveiling Spring AOP: A Deep Dive into Source Code and Proxy Mechanics
This article provides a comprehensive analysis of Spring AOP, covering its core concepts, configuration parsing, BeanFactoryPostProcessor and BeanPostProcessor mechanisms, namespace handling, the creation of advisors and advice from @Aspect annotations, and the proxy generation process that enables method interception.
1 Overview
Spring AOP (Aspect‑Oriented Programming) works on top of the IoC container. Its main feature is intercepting and enhancing specified methods without modifying business code, thus separating business logic from cross‑cutting concerns. In Spring, using transaction annotations automatically opens, commits, or rolls back transactions around business methods.
Match methods that satisfy a pointcut.
Enhance matched methods via JDK dynamic proxies or CGLIB proxies.
2 Case Study
Below is a simple Spring AOP example used for source‑code analysis.
<code>@Aspect
@Component
public class TracesRecordAdvisor {
@Pointcut("execution(* spring.action.expend.aop.services.*.*(..))")
public void expression() {}
@Before("expression()")
public void beforePrint() {
System.out.println("Entering service, logging before execution...");
}
@AfterReturning("expression()")
public void afterPrint() {
System.out.println("Exiting service, logging after execution...");
}
}
</code>XML configuration to enable AOP:
<code><aop:aspectj-autoproxy/></code>Service class (requires an interface for JDK proxy):
<code>@Service
public class PayServiceImpl implements PayService {
public void payMoneyMethod() {
System.out.println("Executing payment...");
}
}
</code>Test method:
<code>@Test
public void springAopTestService() {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring-aop.xml");
PayService payService = (PayService) ctx.getBean("payServiceImpl");
payService.payMoneyMethod();
}
</code>Result:
<code>Entering service, logging before execution...
Executing payment...
Exiting service, logging after execution...
</code>The output shows that payMoneyMethod has been enhanced.
3 BeanFactoryPostProcessor
When reading Spring source code, the BeanFactoryPostProcessor and BeanPostProcessor interfaces allow us to modify BeanDefinition objects after they are parsed from XML but before beans are instantiated.
<code>public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory);
}
</code>The postProcessBeanFactory method receives a ConfigurableListableBeanFactory , which can access all generated BeanDefinition objects. Implementations are registered in the container and invoked during the refresh process.
<code>protected void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
try {
postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
// other initialization steps …
} catch (BeansException ex) {
throw ex;
} finally {
resetCommonCaches();
}
}
}
</code>invokeBeanFactoryPostProcessors iterates over all registered BeanFactoryPostProcessor instances and calls their postProcessBeanFactory method.
4 BeanPostProcessor
Compared with BeanFactoryPostProcessor , BeanPostProcessor is more important because Spring AOP, annotations, etc., rely on it. It intercepts the bean lifecycle after instantiation and before/after initialization.
<code>public interface BeanPostProcessor {
// Called after bean instantiation and property population, before init methods
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
// Called after init methods (e.g., @PostConstruct, afterPropertiesSet)
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
</code>Bean creation sequence:
Instantiate bean (constructor or factory method).
Populate properties.
Invoke Aware interfaces.
postProcessBeforeInitialization .
Invoke init methods.
postProcessAfterInitialization .
The AnnotationAwareAspectJAutoProxyCreator implements both BeanPostProcessor and InstantiationAwareBeanPostProcessor , handling AOP logic during these callbacks.
5 NamespaceHandler
All Spring technologies, including AOP, are built on the IoC container. When the XML parser encounters a tag, it looks up the namespace URI, finds the corresponding NamespaceHandler , and delegates parsing to it.
For the <aop:aspectj-autoproxy/> tag, the handler is AopNamespaceHandler , which registers parsers for config and aspectj-autoproxy elements.
<code>public class AopNamespaceHandler implements NamespaceHandler {
@Override
public void init() {
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
// other methods omitted …
}
</code>The AspectJAutoProxyBeanDefinitionParser simply registers the AnnotationAwareAspectJAutoProxyCreator class.
6 Spring AOP Source Analysis
When Spring reads an XML file containing <aop:aspectj-autoproxy/> , it loads AopNamespaceHandler , which delegates to AspectJAutoProxyBeanDefinitionParser . That parser registers AnnotationAwareAspectJAutoProxyCreator , which implements BeanPostProcessor and InstantiationAwareBeanPostProcessor . The proxy creation flow is:
During bean instantiation, postProcessBeforeInstantiation runs.
After bean initialization, postProcessAfterInitialization runs.
These two callbacks contain the core AOP logic.
7 Annotation Proxy Class Analysis
The class AnnotationAwareAspectJAutoProxyCreator caches aspect classes and infrastructure classes, decides whether a bean should be proxied, and creates the proxy when needed.
<code>public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
Object cacheKey = getCacheKey(beanClass, beanName);
if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
if (this.advisedBeans.containsKey(cacheKey)) {
return null; // already processed as advice
}
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
if (beanName != null) {
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
this.targetSourcedBeans.add(beanName);
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
}
return null;
}
</code>The method first checks whether the class is an aspect or infrastructure class, then whether it should be skipped, and finally creates a proxy if custom TargetSource is present.
8 Aspect Annotation Class Analysis
The postProcessBeforeInstantiation method uses isInfrastructureClass to filter out classes that implement Advice , Advisor , or AopInfrastructureBean . It also checks for the @Aspect annotation.
<code>@Override
protected boolean isInfrastructureClass(Class<?> beanClass) {
return super.isInfrastructureClass(beanClass) || this.aspectJAdvisorFactory.isAspect(beanClass);
}
</code>The parent implementation checks assignability to Advice , Advisor , or AopInfrastructureBean .
9 Getting Advisors for an Aspect
The method getAdvisor builds an Advisor for a given aspect method.
<code>@Override
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) {
AspectJExpressionPointcut pointcut = getPointcut(candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
if (pointcut == null) return null;
return new InstantiationModelAwarePointcutAdvisorImpl(pointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
</code>getPointcut extracts the pointcut expression from annotations such as @Before , @After , etc.
10 Creating Advice for an Aspect Method
Inside ReflectiveAspectJAdvisorFactory.getAdvice , the annotation type determines which concrete Advice implementation is instantiated.
<code>public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
AspectJAnnotation<?> ajAnnotation = findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (ajAnnotation == null) return null;
AbstractAspectJAdvice springAdvice;
switch (ajAnnotation.getAnnotationType()) {
case AtBefore:
springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfter:
springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfterReturning:
springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterReturning ar = (AfterReturning) ajAnnotation.getAnnotation();
if (StringUtils.hasText(ar.returning())) {
springAdvice.setReturningName(ar.returning());
}
break;
case AtAfterThrowing:
springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterThrowing ath = (AfterThrowing) ajAnnotation.getAnnotation();
if (StringUtils.hasText(ath.throwing())) {
springAdvice.setThrowingName(ath.throwing());
}
break;
case AtAround:
springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtPointcut:
// pointcut methods are ignored here
return null;
default:
throw new UnsupportedOperationException("Unsupported advice type on method: " + candidateAdviceMethod);
}
springAdvice.setAspectName(aspectName);
springAdvice.setDeclarationOrder(declarationOrder);
String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
if (argNames != null) {
springAdvice.setArgumentNamesFromStringArray(argNames);
}
springAdvice.calculateArgumentBindings();
return springAdvice;
}
</code>Each concrete Advice class implements MethodInvocation and defines when the aspect method is invoked (before, after, around, etc.).
11 AOP Proxy Overview
After all advisors are collected, postProcessAfterInitialization decides whether to create a proxy.
<code>protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
</code>If matching advisors are found, a JDK dynamic proxy or a CGLIB proxy is created (depending on configuration), and the bean is replaced with the proxy instance.
12 Summary
Spring AOP performs two main tasks during container startup:
Configuration parsing: The XML tag <aop:aspectj-autoproxy/> triggers AopNamespaceHandler , which registers AnnotationAwareAspectJAutoProxyCreator .
Bean creation: postProcessBeforeInstantiation filters out infrastructure and aspect classes. All beans annotated with @Aspect are scanned; their advice methods are turned into Advisor objects and cached. postProcessAfterInitialization checks whether a bean matches any advisor and, if so, creates a proxy that weaves the aspect logic.
The proxy delegates method calls through a chain of MethodInvocation objects, each representing an advice (before, after, around, etc.), thereby achieving transparent method interception.
Architecture & Thinking
🍭 Frontline tech director and chief architect at top-tier companies 🥝 Years of deep experience in internet, e‑commerce, social, and finance sectors 🌾 Committed to publishing high‑quality articles covering core technologies of leading internet firms, application architecture, and AI breakthroughs.
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.