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.
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
AnnotationAwareAspectJAutoProxyCreatorBeanPostProcessor, which handles proxy creation in its
postProcessAfterInitializationmethod.
<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
wrapIfNecessarymethod 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
getAdvicesAndAdvisorsForBeanmethod 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> findEligibleAdvisorslocates 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> findCandidateAdvisorsgathers 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.findAdvisorBeansmethod 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
@DSannotation, 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
CustomAdvisorimplementing
PointcutAdvisorcan 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
shouldSkipmethod decides which classes should not be proxied.
End of article.
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.
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.