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 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();
} findEligibleAdvisorslocates 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;
} findCandidateAdvisorsgathers 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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
