第四篇・Spring 5 源码深度拆解:AOP 全流程核心原理
Spring IOC 核心链路:容器启动 → Bean 生命周期全流程 → 循环依赖与三级缓存
在 Bean 生命周期里:Bean 生命周期中,当 Bean 匹配到 Advisor 时,AOP 代理对象BeanPostProcessor 的 postProcessAfterInitialization 后置处理阶段生成(循环依赖场景下提前在 getEarlyBeanReference 生成)
这一篇,我们拆解 Spring AOP 的源码流程,从开启注解、解析切面,到代理生成、运行时执行:
@EnableAspectJAutoProxy 到底做了什么?-
Spring 怎么扫描解析 @Aspect切面、@Pointcut切点? -
代理对象到底在 Bean 生命周期的哪个节点生成? -
JDK 动态代理和 CGLIB 动态代理怎么选? -
循环依赖场景下,AOP 代理是怎么处理的? -
运行时,切面通知的执行顺序是怎么控制的?
一、先搞懂核心前提:Spring AOP 底层基础
1. AOP 核心概念
|
概念 |
源码对应 |
核心含义 |
|
切面 Aspect |
|
切点 + 通知的集合,定义横向逻辑的类 |
|
切点 Pointcut |
|
定义哪些类 / 方法需要被拦截增强 |
|
通知 Advice |
|
拦截到方法后,要执行的横向逻辑 |
|
通知器 Advisor |
|
Spring AOP 中切点(Pointcut)与通知(Advice)的封装单元,是 Spring 判定 Bean 是否需要代理、如何代理的核心依据,是 AOP 执行的最小逻辑单元 |
|
动态代理 |
/ |
AOP 的底层实现,生成代理对象替代原生 Bean |
2. 两种动态代理的核心区别
Spring AOP 仅支持两种动态代理,源码中会自动选择:
|
特性 |
JDK 动态代理 |
CGLIB 动态代理 |
|
底层实现 |
基于接口,生成实现类的代理 |
基于继承,生成目标类的子类代理 |
|
代理对象生成 |
快 |
慢 |
|
方法执行效率 |
低(反射调用) |
高(字节码生成,无反射) |
|
Spring 默认选择 |
目标类实现了接口时优先使用 |
目标类无接口时使用 |
二、AOP 开启的入口:@EnableAspectJAutoProxy
我们使用 Spring AOP 时,只需要加一个注解:
@Configuration@EnableAspectJAutoProxy // 开启AOP@ComponentScan("com.example")public classSpringConfig{}
这个注解,就是 AOP 所有逻辑的起点。
1. 注解源码拆解
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import(AspectJAutoProxyRegistrar.class) // 核心:导入了注册器public @interface EnableAspectJAutoProxy {// 强制使用CGLIB代理,默认falseboolean proxyTargetClass() default false;// 暴露代理对象到ThreadLocal,解决内部调用AOP失效问题,默认falseboolean exposeProxy() default false;}
2. 核心:AspectJAutoProxyRegistrar 做了什么?
这个注册器的核心作用,就是向 Spring 容器中注册了一个核心 BeanPostProcessor:AnnotationAwareAspectJAutoProxyCreator
关键源码
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {// 核心:注册 AnnotationAwareAspectJAutoProxyCreatorAopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);// 处理 proxyTargetClass、exposeProxy 配置AnnotationAttributes enableAspectJAutoProxy =AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);if (enableAspectJAutoProxy != null) {if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);}if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);}}}}
3. 灵魂核心:AnnotationAwareAspectJAutoProxyCreator
这个类,是 Spring AOP 的绝对核心,它的继承关系:
AnnotationAwareAspectJAutoProxyCreator↓AbstractAdvisorAutoProxyCreator↓AbstractAutoProxyCreator↓实现了 BeanPostProcessor 接口
为什么它是核心?
-
它是一个BeanPostProcessor,会介入每一个 Bean 的生命周期 -
它负责:扫描解析所有 @Aspect切面,生成 Advisor -
它负责:判断 Bean 是否需要生成代理,生成代理对象 -
它就是我们第二篇 Bean 生命周期里,后置处理器中生成 AOP 代理的那个类
三、AOP 全流程第一阶段:容器启动时,切面解析与 Advisor 生成
Spring AOP 不是 Bean 创建时才解析切面,而是容器启动时,就提前扫描解析所有切面,生成 Advisor 通知器,对应我们第一篇的容器启动流程。
完整流程
-
容器启动,执行 refresh() -
执行 invokeBeanFactoryPostProcessors(),解析@EnableAspectJAutoProxy,注册AnnotationAwareAspectJAutoProxyCreator -
执行 registerBeanPostProcessors(),把AnnotationAwareAspectJAutoProxyCreator实例化,加入到 BeanPostProcessor 列表中 AnnotationAwareAspectJAutoProxyCreator
提前扫描所有 @Aspect注解的类,解析切点、通知,封装成一个个Advisor对象,缓存起来,供后续 Bean 创建时使用
核心源码:切面解析
// AnnotationAwareAspectJAutoProxyCreator 核心方法protected List<Advisor> findCandidateAdvisors() {// 1. 先拿到父类的AdvisorList<Advisor> advisors = super.findCandidateAdvisors();// 2. 核心:扫描所有@Aspect注解的类,解析生成Advisoradvisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());return advisors;}
关键细节
-
每个 @Before/@Around/@After等通知,都会被封装成一个独立的Advice(通知);Advice与对应的Pointcut(切点)组合,形成Advisor(通知器)。若多个通知关联同一个切点,会生成多个包含该切点的Advisor,执行顺序由@Order控制 -
每个 Advisor 都包含:切点表达式(匹配哪些方法)+ 通知逻辑(要执行的代码) -
解析完成后,所有 Advisor 会被缓存起来,后续每个 Bean 创建时,直接用这些 Advisor 匹配,判断是否需要生成代理
四、AOP 全流程第二阶段:Bean 创建时,代理对象生成
核心结论先记死:
AOP 代理对象,生成于 Bean 生命周期的「BeanPostProcessor 后置处理阶段」,也就是initializeBean方法的最后一步。
完整流程(对应 Bean 生命周期)
doGetBean → createBean → doCreateBean↓1. createBeanInstance:实例化原生Bean↓2. populateBean:填充属性,依赖注入↓3. initializeBean:初始化• Aware接口执行• BeanPostProcessor前置处理• 初始化方法执行• BeanPostProcessor后置处理【AOP代理生成就在这里!】↓4. 代理对象生成,替换原生Bean,放入单例池
核心源码拆解
1. 入口:后置处理方法
// AbstractAutoProxyCreator(父类)@Overridepublic Object postProcessAfterInitialization(@NullableObject bean, String beanName) {if (bean != null) {Object cacheKey = getCacheKey(bean.getClass(), beanName);// 核心:判断是否需要生成代理,需要则返回代理对象,否则返回原生Beanreturn wrapIfNecessary(bean, beanName, cacheKey);}return bean;}
2. 核心判断:wrapIfNecessary(要不要生成代理)
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {// 1. 提前判断:是否已经处理过,或者是切面类本身,不需要代理if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {return bean;}if (this.advisedBeans.containsKey(cacheKey)) {return this.advisedBeans.get(cacheKey);}// 2. 基础设施类(如 @Aspect 标注的切面类)或需跳过的 Bean,直接返回原生对象if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}// 3. 核心:匹配Advisor,判断当前Bean是否有匹配的切面Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);// 4. 有匹配的切面 → 生成代理对象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());// 返回代理对象,替换原生Beanreturn proxy;}// 5. 没有匹配的切面 → 返回原生Beanthis.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}
3. 生成代理:createProxy
protected Object createProxy(Class<?> beanClass, @NullableString beanName,@Nullable Object[] specificInterceptors, TargetSource targetSource) {// 1. 封装代理配置ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.copyFrom(this);// 处理proxyTargetClass配置,决定用JDK还是CGLIBif (!proxyFactory.isProxyTargetClass()) {if (shouldProxyTargetClass(beanClass, beanName)) {proxyFactory.setProxyTargetClass(true);} else {evaluateProxyInterfaces(beanClass, proxyFactory);}}// 2. 把Advisor加入代理工厂Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);proxyFactory.addAdvisors(advisors);proxyFactory.setTargetSource(targetSource);// 3. 核心:生成代理对象return proxyFactory.getProxy(getBeanClassLoader());}
4. 代理选择逻辑:JDK 还是 CGLIB?
// DefaultAopProxyFactory@Overridepublic AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {// 1. 强制CGLIB、或者目标类无接口 → 用CGLIBif (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {Class<?> targetClass = config.getTargetClass();if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {return new JdkDynamicAopProxy(config);}return new ObjenesisCglibAopProxy(config);}// 2. 目标类实现了接口 → 用JDK动态代理else {return new JdkDynamicAopProxy(config);}}
五、循环依赖场景下,AOP 代理的特殊处理
前面我们介绍循环依赖时,提到了getEarlyBeanReference方法,这个方法就是循环依赖场景下,提前生成 AOP 代理的核心。
问题场景
A ↔ B 循环依赖,且 A 需要生成 AOP 代理:
-
A 实例化后,暴露工厂到三级缓存 -
B 创建时,需要注入 A,从三级缓存拿到 A 的工厂,执行 getEarlyBeanReference -
这里会提前生成 A 的 AOP 代理对象,注入到 B 中 -
A 后续初始化完成后,不会再重复生成代理,保证单例
核心源码
// AbstractAutoProxyCreator@Overridepublic Object getEarlyBeanReference(Object bean, String beanName) {Object cacheKey = getCacheKey(bean.getClass(), beanName);this.earlyProxyReferences.put(cacheKey, bean);// 提前生成代理对象return wrapIfNecessary(bean, beanName, cacheKey);}
关键细节
-
环依赖场景下,代理对象会提前在 getEarlyBeanReference中生成(注入给依赖方),同时将原生 Bean 存入earlyProxyReferences缓存 -
后续执行 postProcessAfterInitialization时,会优先检查该缓存:若存在当前 Bean 的记录,则直接返回已生成的代理对象,避免重复代理 -
这就是为什么三级缓存要存工厂,而不是直接存 Bean 引用 —— 为了支持循环依赖下的 AOP 代理提前生成
六、AOP 全流程第三阶段:运行时,通知链执行
代理对象生成后,当我们调用代理对象的方法时,就会触发切面通知的执行,Spring 通过责任链模式控制通知的执行顺序。
核心执行流程
-
调用代理对象的目标方法 -
进入代理对象的拦截器(JDK 的 InvocationHandler/CGLIB 的MethodInterceptor) -
匹配当前方法的所有 Advisor,生成通知链(Interceptor 链) -
通过 ReflectiveMethodInvocation的proceed()方法,递归执行通知链 -
执行顺序: @Around前置 →@Before→ 目标方法 → (@AfterReturning/@AfterThrowing)→@After→@Around后置。注: @After是最终通知,无论目标方法是否异常,都会在@AfterReturning或@AfterThrowing之后执行。
核心源码:通知链执行
// ReflectiveMethodInvocation@Override@Nullablepublic Object proceed() throws Throwable {// 1. 通知链执行完毕,执行目标方法if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {return invokeJoinpoint();}// 2. 拿到下一个拦截器,递归执行Object interceptorOrInterceptionAdvice =this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {InterceptorAndDynamicMethodMatcher dm =(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {return dm.interceptor.invoke(this);}else {// 不匹配,跳过,执行下一个return proceed();}}else {// 执行拦截器(通知逻辑)return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);}}
七、面试高频问题
1. AOP 代理生成在 Bean 生命周期的哪个阶段?
答:正常场景下,生成于 Bean 生命周期「初始化阶段」的最后一步,即 BeanPostProcessor 的后置处理方法 postProcessAfterInitialization,该方法在初始化方法(@PostConstruct/init-method)执行完成后调用。
2. JDK 动态代理和 CGLIB 动态代理的核心区别?
答:
-
JDK 动态代理基于接口实现,要求目标类必须实现接口,生成接口的实现类代理;CGLIB 基于继承实现,生成目标类的子类代理,要求目标类不能被 final 修饰。 -
JDK 动态代理通过反射执行方法,在极高并发场景下性能略低于 CGLIB,常规业务场景差异不大。 -
Spring 默认策略:目标类实现了接口用 JDK 代理,无接口用 CGLIB 代理,可通过 proxyTargetClass=true强制使用 CGLIB。
3. Spring AOP 内部调用为什么会失效?怎么解决?
答:因为内部调用时,调用的是this原生对象,不是代理对象,不会触发切面逻辑。
解决方法:
-
开启 @EnableAspectJAutoProxy(exposeProxy = true) -
内部调用时,用 ((当前类) AopContext.currentProxy()).目标方法(),通过代理对象调用。
4. @Aspect 切面的执行顺序怎么控制?
答:通过@Order注解控制,value值越小,优先级越高,执行顺序越靠前。前置通知优先级高的先执行,后置通知优先级高的后执行。
1. @EnableAspectJAutoProxy 开启AOP,注册核心处理器↓2. 容器启动,扫描@Aspect切面,解析生成Advisor↓3. Bean创建,初始化完成后,进入后置处理器↓4. 匹配Advisor,判断是否需要代理,需要则生成代理对象↓5. 代理对象替换原生Bean,放入单例池↓6. 运行时调用方法,触发拦截器,执行通知链
九、可直接断点调试的 AOP 完整项目
1. pom.xml 依赖
<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.example</groupId><artifactId>spring-aop-debug</artifactId><version>1.0-SNAPSHOT</version><properties><spring.version>5.3.37</spring.version><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>${spring.version}</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.21</version></dependency></dependencies></project>
2. 项目结构
src├── main│ └── java│ └── com│ └── example│ └── aopdebug│ ├── config│ │ └── SpringAopConfig.java│ ├── service│ │ ├── UserService.java│ │ └── UserServiceImpl.java│ ├── aspect│ │ └── LogAspect.java│ └── AopDebugApplication.java└── pom.xml
3. 完整代码
配置类 SpringAopConfig.java
package com.example.aopdebug.config;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.EnableAspectJAutoProxy;@Configuration@ComponentScan(basePackages = "com.example.aopdebug")@EnableAspectJAutoProxypublic class SpringAopConfig {}
业务接口 UserService.java
package com.example.aopdebug.service;public interface UserService {void addUser(String username);}
业务实现类 UserServiceImpl.java
package com.example.aopdebug.service;import org.springframework.stereotype.Service;@Servicepublic class UserServiceImpl implements UserService {@Overridepublic void addUser(String username) {System.out.println("【目标方法执行】正在添加用户:" + username);}}
切面类 LogAspect.java
package com.example.aopdebug.aspect;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.*;import org.springframework.stereotype.Component;@Aspect@Componentpublic class LogAspect {@Pointcut("execution(* com.example.aopdebug.service.*.*(..))")public void servicePointcut() {}@Before("servicePointcut()")public void before() {System.out.println("【前置通知 @Before】方法执行前");}@AfterReturning("servicePointcut()")public void afterReturning() {System.out.println("【后置返回通知 @AfterReturning】方法正常返回");}@After("servicePointcut()")public void after() {System.out.println("【最终通知 @After】方法执行完毕");}@Around("servicePointcut()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("【环绕通知 @Around 前置】方法执行前");Object result = joinPoint.proceed();System.out.println("【环绕通知 @Around 后置】方法执行后");return result;}}
启动调试类 AopDebugApplication.java
package com.example.aopdebug;import com.example.aopdebug.config.SpringAopConfig;import com.example.aopdebug.service.UserService;import org.springframework.aop.support.AopUtils;import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class AopDebugApplication {public static void main(String[] args) {AnnotationConfigApplicationContext context =new AnnotationConfigApplicationContext(SpringAopConfig.class);UserService userService = context.getBean(UserService.class);// 查看代理对象信息System.out.println("Bean真实类型:" + userService.getClass().getName());System.out.println("是否是AOP代理:" + AopUtils.isAopProxy(userService));System.out.println("是否是JDK代理:" + AopUtils.isJdkDynamicProxy(userService));System.out.println("是否是CGLIB代理:" + AopUtils.isCglibProxy(userService));// 触发AOP通知执行userService.addUser("张三");context.close();}}
4. 核心断点位置
|
断点类全称 |
断点方法 |
调试内容 |
|
|
|
切面解析与 Advisor 生成 |
|
AbstractAutoProxyCreator |
postProcessAfterInitialization() |
代理生成入口 |
|
AbstractAutoProxyCreator |
|
代理匹配判断 |
|
DefaultAopProxyFactory |
|
代理类型选择 |
|
JdkDynamicAopProxy |
|
运行时通知链执行 |
下篇预告
下一篇我们进入 Spring 最核心的业务场景考点:
Spring 事务源码全流程拆解
@Transactional 注解到底做了什么?-
事务的传播行为、隔离级别源码实现 -
事务失效的 9 种场景,从源码层面说清为什么 -
事务与 AOP 的关联关系
如您在文章中发现有错误的地方,欢迎指正。
夜雨聆风