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.

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;
}

2. The wrapIfNecessary method decides whether the current bean needs to be wrapped.

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;
}

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

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();
}
findEligibleAdvisors

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

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;
}
findCandidateAdvisors

gathers Advisors from the container and builds AspectJ advisors.

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;
}

The BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans method retrieves all Advisor bean names from the container.

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
}

To intercept methods annotated with a custom @DS annotation, a custom Advisor can be defined.

@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("更新数据...");
    }
}

A simple CustomAdvisor implementing PointcutAdvisor can provide the advice and pointcut logic.

@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; }
        };
    }
}

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.

@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();
    }
}

The shouldSkip method decides which classes should not be proxied.

End of article.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

ProxyaopspringAdvisorCustomAdvisor
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

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.