Backend Development 8 min read

How Spring Builds AOP Proxies: Advisor Discovery and Custom Proxy Creation

This article explains how Spring 5.3.3 creates AOP proxy objects using Advisors, details the internal methods for locating and applying both bean-defined and @Aspect advisors, and demonstrates how to implement a custom Advisor to intercept methods annotated with @DS, including common pitfalls and solutions.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
How Spring Builds AOP Proxies: Advisor Discovery and Custom Proxy Creation

Environment: Spring 5.3.3

Spring creates proxy objects through an Advisor; if no Advisor applies, no proxy is created, whether the Advisor is custom or defined via @Aspect.

Proxy Object Creation

1. After enabling AOP, Spring registers the

AnnotationAwareAspectJAutoProxyCreator

BeanPostProcessor, which handles proxy creation in its

postProcessAfterInitialization

method.

<code>public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}</code>

2. The

wrapIfNecessary

method decides whether the current bean needs to be wrapped.

<code>protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // other code
    // Create proxy if we have advice.
    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>

The

getAdvicesAndAdvisorsForBean

method checks whether any Advisor exists for the bean; only then is a proxy created.

<code>protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
    List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
    if (advisors.isEmpty()) {
        return DO_NOT_PROXY;
    }
    return advisors.toArray();
}</code>
findEligibleAdvisors

locates all eligible Advisors, which may be regular beans or @Aspect‑annotated classes.

<code>protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}</code>
findCandidateAdvisors

gathers Advisors from the container and builds AspectJ advisors.

<code>protected List<Advisor> findCandidateAdvisors() {
    // Add all the Spring advisors found according to superclass rules.
    List<Advisor> advisors = super.findCandidateAdvisors();
    if (this.aspectJAdvisorsBuilder != null) {
        advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
    }
    return advisors;
}</code>

The

BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans

method retrieves all Advisor bean names from the container.

<code>public List<Advisor> findAdvisorBeans() {
    String[] advisorNames = this.cachedAdvisorBeanNames;
    if (advisorNames == null) {
        advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
            this.beanFactory, Advisor.class, true, false);
        this.cachedAdvisorBeanNames = advisorNames;
    }
    // other code
}</code>

To intercept methods annotated with a custom

@DS

annotation, a custom Advisor can be defined.

<code>@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface DS {}

public interface CustomDAO {
    void update();
}

@Component
public class CustomerDAOImpl implements CustomDAO {
    @DS
    public void update() {
        System.out.println("更新数据...");
    }
}</code>

A simple

CustomAdvisor

implementing

PointcutAdvisor

can provide the advice and pointcut logic.

<code>@Component
public class CustomAdvisor implements PointcutAdvisor {
    @Override
    public Advice getAdvice() {
        return new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                System.out.println("我被调用了...");
                return invocation.proceed();
            }
        };
    }
    @Override
    public boolean isPerInstance() { return true; }
    @Override
    public Pointcut getPointcut() {
        return new Pointcut() {
            @Override
            public MethodMatcher getMethodMatcher() {
                return new MethodMatcher() {
                    @Override
                    public boolean matches(Method method, Class<?> targetClass, Object... args) { return false; }
                    @Override
                    public boolean matches(Method method, Class<?> targetClass) {
                        return method.isAnnotationPresent(DS.class);
                    }
                    @Override
                    public boolean isRuntime() { return false; }
                };
            }
            @Override
            public ClassFilter getClassFilter() { return ClassFilter.TRUE; }
        };
    }
}
</code>

When only this Advisor is used, the method may not be intercepted because Spring defaults to JDK dynamic proxies, which proxy the interface method rather than the implementation method containing @DS.

Two solutions:

Add @DS on the interface method.

Force CGLIB proxying (e.g., set

proxyTargetClass=true

).

Extending AbstractAutoProxyCreator

By extending

AbstractAutoProxyCreator

, you gain finer control over which beans are proxied.

<code>@Component
public class PackScanner extends AbstractAutoProxyCreator {
    private static final long serialVersionUID = 1L;
    @Resource
    private Advisor customAdvisor;
    @Override
    protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource customTargetSource) throws BeansException {
        return new Object[] { new CustomAdvisor() };
    }
    @Override
    protected boolean shouldSkip(Class<?> beanClass, String beanName) {
        return !"customerDAOImpl".equals(beanName);
    }
    @Override
    public boolean isProxyTargetClass() {
        return super.isProxyTargetClass();
    }
}
</code>

The

shouldSkip

method decides which classes should not be proxied.

End of article.

JavaproxyaopSpringAdvisorCustomAdvisor
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.