高密市网站建设,和田地区建设局网站,wordpress确认窗口,网营科技Spring AOP总结 更美观清晰的版本在#xff1a;Github 前面的章节#xff1a; [Spring AOP 1] 从零开始的JDK动态代理 [Spring AOP 2] 从零开始的CGLIB动态代理 [Spring AOP 3] Spring选择代理 [Spring AOP 4] Spring AOP 切点匹配 [Spring AOP 5] 高级切面与低级切面#…Spring AOP总结 更美观清晰的版本在Github 前面的章节 [Spring AOP 1] 从零开始的JDK动态代理 [Spring AOP 2] 从零开始的CGLIB动态代理 [Spring AOP 3] Spring选择代理 [Spring AOP 4] Spring AOP 切点匹配 [Spring AOP 5] 高级切面与低级切面Aspect vs Advisor [Spring AOP 6] 静态通知调用 [Spring AOP 7] 动态通知调用 我们以日常在Spring Boot中编写AOP进行方法增强的代码为例来整体梳理AOP背后到底发生了什么。我们需要
配置类开启代理切面类这里准备了一个前置通知和一个环绕通知目标类启动类
Configuration
EnableAspectJAutoProxy(proxyTargetClass true) // 将会使用CGLIB代理
ComponentScan(basePackages spring.aop.summarization)
public class AopConfig {}Slf4j
Aspect
Component
public class MyAspect {// 前置增强foo方法Before(execution(* spring.aop.summarization.Target.foo(..)))public void beforeAdvice(JoinPoint joinPoint) {log.info(Foo is ready to do something...);}// 环绕增强bar方法Around(execution(* spring.aop.summarization.Target.bar(..)))public Object executionTime(ProceedingJoinPoint joinPoint) throws Throwable {log.info(Bar is ready to do something...);long start System.currentTimeMillis();Object result joinPoint.proceed();long end System.currentTimeMillis();log.info(Bar execution time: {}, end - start);return result;}
}Component
Slf4j
public class Target {public void foo() {fooDoSomething();}public void bar() {barDoSomething();}public void fooDoSomething() {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}public void barDoSomething() {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}
}SpringBootApplication
public class MyApplication {public static void main(String[] args) {ConfigurableApplicationContext context SpringApplication.run(MyApplication.class, args);Target target (Target) context.getBean(target); // 获得代理对象target.foo(); // 代理对象将调用增强了的方法target.bar();}
}我们运行启动类可以看到预期的输出结果
foo方法被前置增强bar方法被环绕增强
2025-05-08T11:16:12.38201:00 INFO 13260 --- [ main] spring.aop.summarization.MyAspect : Foo is ready to do something...
2025-05-08T11:16:13.38401:00 INFO 13260 --- [ main] spring.aop.summarization.MyAspect : Bar is ready to do something...
2025-05-08T11:16:14.38601:00 INFO 13260 --- [ main] spring.aop.summarization.MyAspect : Bar execution time: 1002接下来我们主要关注Spring AOP中发生的事情。 解析切面定义 Spring 启动时扫描所有 Aspect 注解的类如 MyAspect并把带有 Before、Around、After 等方法标记的元信息提取出来交给一个 ReflectiveAspectJAdvisorFactory 去生成“候选” Advisor。 组装低级 Advisor ReflectiveAspectJAdvisorFactory 会将每个切面方法分装为一个 Pointcut Advice 对于 Before(…)生成一个 AspectJExpressionPointcut 和一个 AspectJMethodBeforeAdvice再封装成一个 DefaultPointcutAdvisor对于 Around(…)生成一个 AspectJExpressionPointcut 和一个 AspectJAroundAdvice再封装成另一个 DefaultPointcutAdvisor所有这些 Advisor 都被当成候选切面缓存下来等待后续匹配。同时Spring 也注册了一个关键的 BeanPostProcessorAnnotationAwareAspectJAutoProxyCreator。 AnnotationAwareAspectJAutoProxyCreator后续会根据切面类型自动生成代理对象 容器刷新 BeanPostProcessor 注册 当 SpringApplication.run(...) 完成扫描和注册后容器会把 AnnotationAwareAspectJAutoProxyCreator 等所有 BeanPostProcessor 都注册到生命周期中。 Bean 实例化与初始化省略细节 对每一个普通 Bean比如Target Spring会用构造器创建实例并进行依赖注入 如果Bean没有循环依赖那么AnnotationAwareAspectJAutoProxyCreator创建代理对象的时机在Bean初始化后如果Bean被检测到有循环依赖那么AnnotationAwareAspectJAutoProxyCreator创建代理对象的时机在依赖注入前 进行Bean的后处理进入AOP代理逻辑 自动代理 (wrapIfNecessary) 在Bean的后处理中 AnnotationAwareAspectJAutoProxyCreator#wrapIfNecessary判断 这个类是不是基础设施切面本身、Advisor、Advice 等如果是跳过否则执行findEligibleAdvisors() 调用 findEligibleAdvisors()它会拿之前缓存的所有候选 Advisor对每一个执行静态匹配找到所有匹配当前 Bean 的切面若结果不为空就新建一个 ProxyFactory决定用 JDK 代理还是 CGLIB给它设置目标对象和接口/类信息 proxyTargetClass false 目标实现接口JDKproxyTargetClass false 目标未实现接口CGLIBproxyTargetClass trueCGLIB 把上一步过滤出的 Advisor 全部加到 ProxyFactory调用 proxyFactory.getProxy()生成代理对象并替换掉容器中原来的 Bean 引用。 代理对象注入 以后别的 Bean 依赖 Target 时Spring 注入的都是上面生成的代理JdkDynamicAopProxy 或 ObjenesisCglibDynamicAopProxy而不是直接的 new Target()。 运行时方法调用 拦截链执行 当调用 target.foo() 或 target.bar()时 代理对象拦截调用进入 JdkDynamicAopProxy#invoke或 CglibAopProxy#intercept 先执行AdvisedSupport#getInterceptorsAndDynamicInterceptionAdvice() 将所有通知统一转换为静态通知或者拿到动态通知但我们这个例子中没有动态通知 再先根据 Advisor 列表为该方法构建 MethodInterceptor 链 ExposeInvocationInterceptor会创建一个最外围的ADVISOR切面同时会将调用链存入自己的ThreadLocal中 调用 ReflectiveMethodInvocation.proceed()沿链执行递归 Before AdviceBefore── 执行 beforeAdvice()Around AdviceAround── 调用 proceed() 前逻辑目标方法Target#foo / bar实际上被调用开始一层层退出Around Advice后逻辑被执行退出Before Advice 链条执行完毕最终结果返回给调用者。