skip to content

How does AnnotationAwareAspectJAutoProxyCreator actually create proxies, and why is it a BeanPostProcessor?

level: middleimportance: must knowfreq 55%

answer

  1. BPP = replace bean with proxy
  2. postProcessAfterInitialization -> wrapIfNecessary
  3. Advisors from @Aspect methods
  4. isInfrastructureClass skips aspects
  5. getEarlyBeanReference for circular refs

basics

~20 s

It's a BeanPostProcessor, so Spring runs it after each bean is created. In postProcessAfterInitialization it checks if any aspect's pointcut matches the bean; if so it returns an AOP proxy that wraps the bean instead of the raw object.

solid answer

~40 s

AnnotationAwareAspectJAutoProxyCreator is a BeanPostProcessor — specifically a SmartInstantiationAwareBeanPostProcessor. Being a BeanPostProcessor is the mechanism: the container invokes post-processors on every bean after instantiation, so the creator gets a chance to swap the raw bean for a proxy. At startup it builds Advisors from @Aspect beans (and any Spring Advisor beans). In postProcessAfterInitialization it evaluates each bean against all advisor pointcuts; if at least one matches, it uses a ProxyFactory to build a JDK or CGLIB proxy, chaining the matching advisors as an interceptor chain. The container then hands out the proxy. Because it also implements getEarlyBeanReference, it can proxy a bean early to handle circular dependencies. Infrastructure beans and the aspects themselves are skipped (isInfrastructureClass), which is why not every bean gets wrapped.

code

java · 20 lines
java
// Conceptual sketch of the auto-proxy creator's core callback
class AbstractAutoProxyCreator implements SmartInstantiationAwareBeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
        return bean; // already proxied early (circular ref) -> don't double-wrap
    }

    private Object wrapIfNecessary(Object bean, String name, Object key) {
        Object[] advisors = getAdvicesAndAdvisorsForBean(bean.getClass(), name, null);
        if (advisors == DO_NOT_PROXY) {
            return bean; // no pointcut matched -> raw bean
        }
        return createProxy(bean.getClass(), name, advisors, new SingletonTargetSource(bean));
    }
}

go deeper

for a junior

Know it wraps matched beans after they're built.

for a middle

Explain the BPP callback, advisor matching, and no-match = raw bean.

for a senior

Add getEarlyBeanReference / circular dependencies and infrastructure-bean skipping.

for a principal

Discuss ordering hazards, the 'not eligible for all BPPs' warning, and TargetSource/ProxyFactory internals.

## Why a BeanPostProcessor? Spring needs a hook that runs **for every bean, after it is fully created**, and can **replace** the bean the container hands out. That is exactly what a `BeanPostProcessor` (BPP) provides via its two callbacks, `postProcessBeforeInitialization` and `postProcessAfterInitialization`. AOP proxying is implemented as a BPP so it can transparently substitute a proxy for the original object without any bean knowing. `AnnotationAwareAspectJAutoProxyCreator` extends `AspectJAwareAdvisorAutoProxyCreator` → `AbstractAdvisorAutoProxyCreator` → `AbstractAutoProxyCreator`, and implements `SmartInstantiationAwareBeanPostProcessor` (a BPP subtype). ### Startup: building advisors - **Advisor** = a pointcut + advice pair (Spring's internal unit of AOP). - The "AnnotationAware" part means it understands **@AspectJ** annotations: it scans for `@Aspect` beans and, via `BeanFactoryAspectJAdvisorsBuilder`, converts each `@Before/@After/@Around/@AfterReturning/@AfterThrowing` method into an Advisor. It also picks up native Spring `Advisor` beans (e.g. those registered by `@EnableTransactionManagement`). ### Per bean: deciding to proxy Inside `postProcessAfterInitialization(bean, beanName)`: 1. Skip if the bean is infrastructure (an Advisor, Advice, Pointcut, AopInfrastructureBean, or an `@Aspect` itself) — `isInfrastructureClass` / `shouldSkip`. This is why aspects and post-processors are never themselves wrapped. 2. Find advisors whose pointcut matches the bean's class/methods (`getAdvicesAndAdvisorsForBean`). 3. If none match → return the bean unchanged (no proxy, zero overhead). 4. If some match → call `createProxy`, which uses a `ProxyFactory` to produce a proxy backed by the target and an interceptor chain of the matched advisors. ### Proxy type - Bean implements at least one interface **and** `proxyTargetClass=false` → **JDK dynamic proxy** (implements the interfaces). - No interface, or `proxyTargetClass=true` → **CGLIB** subclass proxy. ### Circular dependencies Because it implements `SmartInstantiationAwareBeanPostProcessor`, it overrides `getEarlyBeanReference`. When bean A and B depend on each other, Spring may need to expose A's proxy *before* A finishes initializing. The creator proxies early and records the bean in `earlyProxyReferences` so `postProcessAfterInitialization` does not wrap it a second time. ### Gotcha: ordering and un-proxied beans - Beans referenced by a BeanPostProcessor (or by the aspect infrastructure) can be instantiated **before** the auto-proxy creator is ready, and Spring logs "is not eligible for getting processed by all BeanPostProcessors" — such beans may never be proxied. - Injecting a bean during another bean's construction can likewise capture a not-yet-proxied instance. ### When it matters in interviews Understanding it is a BPP explains: why only some beans are proxied, why aspects can't advise themselves, why proxying is a startup concern, and how circular-dependency proxying works.

  • Why can an aspect never advise itself or another aspect?
    The creator's shouldSkip/isInfrastructureClass treats @Aspect classes, Advisors, Advices, and AopInfrastructureBeans as infrastructure and skips proxying them, so their own methods are never intercepted.
  • Why do some beans log 'not eligible for getting processed by all BeanPostProcessors'?
    They were instantiated (often as dependencies of a BeanPostProcessor or the AOP infrastructure) before the auto-proxy creator was active, so it couldn't wrap them — meaning aspects won't apply to those beans.

saying these in an interview costs you the question

  • Saying proxies are created at compile time or by bytecode weaving
  • Claiming every bean gets a proxy regardless of pointcut matches
  • Not knowing it's a BeanPostProcessor
  • Thinking aspects can advise other aspects

context