新闻详情

新闻详情

首页 / 资讯中心 / 详情

Spring核心机制总结:IOC、Bean生命周期、三级缓存与AOP实战解析

发布时间:2026/10/1 15:59:46来源:尧图网络
Spring核心机制总结:IOC、Bean生命周期、三级缓存与AOP实战解析
Spring 这个框架我前后用了快十年从最早的 SSH 时代一路做到 Spring Boot、Spring Cloud再到 Spring AI 这种 AI 应用接入真没见过哪个 Java 生态的框架能这样长盛不衰。网上讲 Spring 的资料浩如烟海但大多要么太浅、要么太散真遇到问题还是得自己翻源码、打日志。这篇文章我把它称为“Spring 总结(上)”就是想把这么多年实战里沉淀下来的核心认知整理出来重点放在 Spring 最本质的东西上——IOC 容器、Bean 生命周期、三级缓存、AOP 机制、以及那些容易被忽略的扩展点。这些东西搞透了再看 Spring Boot 的自动配置、Spring Cloud 的微服务体系你会发现都是在这些地基上盖房子。这篇文章适合两类人一是准备面试、想把 Spring 原理讲清楚的同学二是已经在写业务代码、但遇到循环依赖报错、事务失效、代理不生效这类问题需要系统理解底层逻辑的开发者。我会尽量把每个机制背后的“为什么”讲透而不是只罗列结论。1. Spring 的核心根基IOC 容器与 Bean 生命周期1.1 IOC 到底是什么别把“控制反转”背成口号很多人在简历上写“熟悉 IOC”问起来就一句话把对象创建和依赖管理的控制权交给 Spring 容器。这句话没错但太笼统。IOC 的本质是一套对象管理基础设施它解决的不只是“谁来 new 对象”的问题而是三件事的集合对象创建、依赖装配、生命周期管理。你可以把容器想象成一个带完整管理系统的工厂仓库——不只是把货物Bean放进去还管货物什么时候进货、怎样组装、何时销毁、坏了怎么处理。控制反转具体反转了什么最直观的是依赖获取方式的反转。传统写法是 Service 里自己 new Dao这叫控制权在自己手里。用 Spring 之后你只需要声明“我需要一个 Dao”容器就会在合适的时机把它注入进来。这就是“依赖注入”——控制反转的实现手段。但还有一层更深的反转常常被忽略对象生命周期的控制权也交给容器了。你不再手动决定单例对象何时创建、何时销毁而是由容器来管理这也正是后面要讲的三级缓存、循环依赖等系列机制存在的前提。Spring 官方文档里其实提到过IOC 容器就是基于BeanFactory和ApplicationContext两套接口体系实现的。BeanFactory是底层容器懒加载、按需获取ApplicationContext在其基础上增加了国际化、事件传播、资源加载、自动注册BeanPostProcessor等能力我们平时用的基本都是后者。在分析 Spring 源码时建议始终记住ApplicationContext就是在BeanFactory之上叠加了企业级能力——这个理解能帮你在阅读源码时快速定位目标。1.2 Bean 生命周期一张时间线串起所有关键回调Bean 生命周期是 Spring 面试必考、也是排查问题绕不开的基础。我把完整的生命周期整理成一条时间线每个阶段都会触发特定的回调理解这条线你就能解释很多看似诡异的现象。第一步是实例化容器根据 BeanDefinition 创建对象实例这一步只是 new 出一个对象属性都是空的。第二步是属性填充也就是依赖注入Autowired、Resource、XML 里的 property 都是在这一阶段处理。第三步是Aware 回调如果 Bean 实现了BeanNameAware、BeanFactoryAware、ApplicationContextAware等接口会在这里拿到容器相关信息。第四步是BeanPostProcessor 的 postProcessBeforeInitialization这是个极其重要的扩展点很多框架的底层功能都挂在这里。第五步是InitializingBean 和自定义 init-method执行初始化逻辑。第六步是BeanPostProcessor 的 postProcessAfterInitializationAOP 动态代理的创建就在这里发生——这是理解代理 Bean 的关键节点。第七步是 Bean 就绪可以被使用了。第八步是容器关闭时执行DisposableBean和 destroy-method 完成销毁。这个顺序我建议大家背下来尤其是第四和第六步的位置。因为在实际应用中一个常见的坑是你在PostConstruct里调用的方法如果被 AOP 增强了那在postProcessAfterInitialization之前调用到的其实是原始对象而不是代理对象。网上很多“PostConstruct 里调用事务方法不生效”的案例根因就出在生命周期节点上。为了方便对照我画个表格总结各阶段和核心扩展点的对应关系生命周期阶段主要动作核心扩展点/接口实例化创建原始对象InstantiationAwareBeanPostProcessor属性填充依赖注入AutowiredAnnotationBeanPostProcessorAware 回调注入容器信息BeanNameAware、ApplicationContextAware初始化前自定义预处理BeanPostProcessor.postProcessBeforeInitialization初始化执行初始化逻辑InitializingBean、PostConstruct、init-method初始化后AOP 代理创建BeanPostProcessor.postProcessAfterInitialization使用业务调用业务代码销毁释放资源DisposableBean、PreDestroy、destroy-method1.3 从“手写 Spring”角度理解 BeanDefinition 与注册表热词里有“手写 Spring”和“Spring 底层源码解析”我强烈建议每个想深入理解 Spring 的人都尝试用几百行代码实现一个迷你版容器。不是为了重复造轮子而是为了让你彻底理解容器处理 Bean 的流程。最核心的抽象是BeanDefinition。它不是一个 Bean 实例而是描述 Bean 如何创建的元数据包括类名、作用域singleton/prototype、是否懒加载、初始化方法名、属性值等。容器先解析配置把每个 Bean 变成 BeanDefinition 注册到注册表里然后才根据 BeanDefinition 去实例化。这就是为什么BeanFactoryPostProcessor能在 Bean 创建之前修改 Bean 的定义——它操作的是 BeanDefinition而不是已经创建好的对象。我见过不少新人混淆BeanFactoryPostProcessor和BeanPostProcessor这里做个明确区分前者处理的是“生产计划”BeanDefinition在 Bean 实例化之前执行后者处理的是“已生产的产品”Bean 实例在 Bean 初始化前后各执行一次。理解这个区别你就能看懂为什么PropertySourcesPlaceholderConfigurer处理占位符属于前者而AutowiredAnnotationBeanPostProcessor处理自动注入属于后者。当你自己手写一遍DefaultListableBeanFactory的getBean流程你会深刻地理解一个事实Spring 的 Bean 创建远不是一次简单的 new而是经过多层判断和扩展点处理的复杂管线。这也能解释为什么同一个类在不同生命周期阶段会有不同形态原始对象、代理对象等。2. 循环依赖与三级缓存Spring 最精妙的设计之一2.1 循环依赖产生的原因和处理思路循环依赖说白了就是 A 依赖 BB 又依赖 A在创建 A 时发现需要 B而创建 B 时又发现需要 A如果不做特殊处理双方都在等待对方创建完成死锁就出现了。日常开发中常见的场景是两个 Service 互相调用对方的方法虽然设计上可以避免但真实的业务里总有绕不开的时候。解决循环依赖的朴素思路其实很简单先让 A 的半成品对象暴露出来再去创建 BB 拿到 A 的引用后完成创建最后再回去把 A 补充完整。这就是“提前暴露”思想。Spring 的缓存体系就是围绕这个思路设计的。需要强调的前提是Spring 只能解决单例模式、且不是构造器注入的循环依赖。为什么因为只有单例 Bean 才存在缓存的必要只有属性注入才允许对象先创建、后填充构造器注入在实例化阶段就必须拿到完整依赖没有提前暴露的机会。所以当你遇到BeanCurrentlyInCreationException时先检查是不是构造器注入导致的循环依赖——这种情况 Spring 确实无能为力只能重构代码。2.2 三级缓存各自的职责以及为什么不能合并成两级Spring 的DefaultSingletonBeanRegistry里有三个 Map这就是所谓的“三级缓存”。我直接说结论一级缓存singletonObjects存放完整的成品 Bean二级缓存earlySingletonObjects存放提前暴露的早期 Bean半成品三级缓存singletonFactories存放的是ObjectFactory也就是一个“生产早期引用的工厂”。为什么需要三级而不是两级这里有一个很关键的场景**当 Bean 会被 AOP 代理时最终放进一级缓存的应该是代理对象而不是原始对象。**问题是 AOP 代理通常是在 Bean 初始化完成后的postProcessAfterInitialization阶段创建的但如果 B 在创建时需要引用 A此时 A 还没走到初始化完成代理也还没生成。如果不做特殊设计B 拿到的 A 是原始对象而最终容器里的 A 是代理对象同一个 Bean 在系统里存在两份不同的引用这会导致事务、权限等基于 AOP 的功能全部失效。三级缓存的ObjectFactory就是为了解决这个矛盾。它的getObject()内部会调用getEarlyBeanReference方法在这个方法里Spring 会提前检查该 Bean 是否被切面增强如果命中切面就提前生成代理对象并返回。这样 B 拿到的引用就是代理对象的早期引用后续 A 继续初始化完成后容器确保最终使用的也是同一个代理对象引用一致性就保住了。那么问题来了二级缓存有没有必要单独存在在纯无代理场景下二级缓存的确看起来“多余”直接从三级缓存拿工厂再生成对象也行。但考虑到性能ObjectFactory.getObject()不只是 new 一下可能触发代理判断、后置处理等逻辑如果每次都调用工厂重新生成开销不可控。所以二级缓存本质上是结果缓存——把工厂生产出来的早期引用缓存下来后续直接复用避免重复执行工厂逻辑。这也解释了为什么 Spring 选择三级缓存而不是两级早期暴露的对象需要稳定复用同时要保证代理对象的引用一致这两个诉求靠 Map 的职责分离来实现。我把三级缓存的层次整理成一张表方便记忆缓存级别存储结构存储内容用途一级缓存singletonObjects完整的成品 Bean正常获取 Bean 时读取二级缓存earlySingletonObjects早期暴露的 Bean/代理解决循环依赖时的中间引用三级缓存singletonFactoriesObjectFactory延迟生成早期引用支持代理提前暴露2.3 Spring Boot 的默认配置坑循环依赖默认被禁止这一点是后来才遇到的坑值得单独拎出来说。Spring Boot 2.6 版本之后循环依赖默认是不允许的——你在配置里不主动开启容器检测到循环依赖会直接报错。这对老项目升级特别不友好很多跑得好好的项目一升级就崩。如果确实有循环依赖需要兼容需要在application.yml里显式设置spring: main: allow-circular-references: true但我的建议是能不开就不开。循环依赖本质上说明你的类设计存在耦合优先调整结构比如通过构造器重构、引入中间层、使用事件解耦来打破依赖环。实际项目里我碰到过多个因为循环依赖导致启动极慢、排查困难的情况——Spring 每次创建 Bean 都要走缓存判断依赖环越多容器创建顺序越难以预测。记住一句话允许循环引用是兜底方案不是默认方案。3. AOP 的实现机制代理模式与切面失效场景3.1 JDK 动态代理与 CGLIB为什么 Spring Boot 默认选 CGLIBAOP 的实现本质是代理模式。Spring 里有两个代理工厂一个是ProxyFactory一个是更底层的ProxyCreatorSupport。选择 JDK 动态代理还是 CGLIB决定了代理的形态和限制。JDK 动态代理基于接口生成的代理类实现同一接口通过InvocationHandler转发方法调用。它的硬性要求是目标类必须实现接口。CGLIB 则是通过继承目标类生成子类在子类里重写父类方法实现增强。CGLIB 的优势是不需要接口就能代理缺点是 final 方法无法被重写也就无法被代理生效。Spring Boot 2.x 之后spring.aop.proxy-target-class默认为 true也就是默认使用 CGLIB。这个选择的原因很实际大量业务类并没有刻意设计接口接口驱动会让代码变得繁琐。但这也带来新的限制——类不能是 final、目标方法不能是 final。我强调过很多次你写了一个 final 方法以为 Spring 能帮你做事务和日志增强结果完全没有生效找半天问题都没想到是 final 的锅。另外要补充一个细节CGLIB 生成的代理类在运行时如果被强转成具体类是可以的因为代理类本身就是目标类的子类。但如果通过 JDK 动态代理生成的对象强转为具体类就会抛ClassCastException——它只实现了接口并不是目标类的子类。这个区别在日常编码、特别是在做 Bean 类型判断的时候很容易踩中。3.2 AOP 失效的四个高频场景AOP 失效是实际业务中最常见的疑难杂症我见得最多的有四类第一是同类内部方法调用。this.method()根本不会经过代理对象而是直接调用目标对象自身的方法。比如一个 Service 里的方法 A 调同类的方法 BB 上有Transactional注解事务大概率不会生效。解决办法有三个拆到另一个 Service 注入使用使用TransactionTemplate手动控制事务或者在类里注入自身代理Resource自引用配合循环依赖开启来调用。最推荐的是前两个。第二是final 方法和 final 类CGLIB 无法重写 final 方法事务和日志增强会直接失效。这个前面已经讲过。第三是方法访问权限受限。CGLIB 子类重写父类方法时不能访问父类的 private 方法所以 private 方法上标注Transactional也是无效的。Spring 官方文档也提示过事务注解建议放在 public 方法上不要在 private 或 protected 方法上期望 AOP 生效。第四是代理对象未被正确持有。你手动的new出来的对象或者从框架外部直接获取的对象都不会被 Spring 代理。还有一个隐蔽的场景如果配置了EnableAspectJAutoProxy(exposeProxy false)在类内部使用AopContext.currentProxy()时会报错因为线程上下文里没有暴露代理对象。需要内部调用时把exposeProxy设为 true然后用((Service) AopContext.currentProxy()).methodB()来调用。3.3ProxyFactory源码视角Advisor 与切面的组装过程热词里有 “Spring 底层 ap 源码解析。proxy factory”这里我补充一个偏源码向的解读。在 Spring 中ProxyFactory是创建代理的核心入口ProxyFactory可以手动配置切面Advisor、接口、目标对象等然后调用getProxy()获得代理实例。在代理创建过程中AdvisedSupport会收集所有匹配的 Advisor。一个Advisor是切面Pointcut与通知Advice的组合体。Pointcut负责匹配方法Advice负责定义增强逻辑。Spring 有一个非常关键的动作在DefaultAopProxyFactory.createAopProxy里判断使用 JDK 还是 CGLIB——如果目标类有接口且未强制 CGLIB就选 JDK 动态代理否则选 CGLIB。判断逻辑很简单但很多人不知道的是如果目标类没有接口即便你想用 JDK 动态代理也是不可能的Spring 会自动降级到 CGLIB。所以如果 Spring Boot 里配置了proxy-target-classfalse业务类却没有任何接口启动时你可能看到 CGLIB 代理仍然被创建。代理方法执行时会沿着拦截器链逐个执行。ExposeInvocationInterceptor会把当前方法调用信息绑定到线程上下文TransactionInterceptor负责事务开启、提交、回滚MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor等各司其职。你看到的代理对象输出里有一串拦截器就是这个组装结果。建议有时间的同学自行 Debug 一下ProxyFactory.getProxy()的调用链先看AdvisedSupport如何收集通知器再看DefaultAopProxyFactory如何选择代理方式最后看代理对象创建后执行拦截器链的顺序。走完一遍Spring AOP 在你眼里就没有秘密了。4. 扩展机制与 Spring AI 生态的基础BeanPostProcessor 与事件驱动4.1BeanPostProcessorSpring 最强扩展点框架都在用它如果要我说 Spring 里最值得研究的扩展点第一是BeanPostProcessor第二是BeanFactoryPostProcessor第三才是事件机制。前者的强大之处在于每个 Bean 在初始化前后都会经过它的处理这意味着你可以在这个环节对任意 Bean 做增强、包装、替换或代理。举个例子Async注解之所以能够生效是因为AsyncAnnotationBeanPostProcessor在postProcessAfterInitialization阶段把目标 Bean 包装成了代理对象异步方法被调度器接管。Autowired注解的解析依赖AutowiredAnnotationBeanPostProcessor它处理的是属性填充阶段。Scheduled定时任务也是通过类似机制把任务注册到调度器里的。如果你要写一个组件在容器里对所有某种类型的 Bean 做统一增强BeanPostProcessor就是你的首选。我在公司内部做过一个类似的组件所有实现了某接口的 Bean启动时自动把暴露的方法注册到元数据中心。实现方式就是在postProcessAfterInitialization里判断类型匹配后提取元数据注册既不需要业务代码额外配合也不会侵入现有逻辑。这里有个细节注意你自己定义的BeanPostProcessor本身也是一个 Bean但它不能对自己的初始化前后再做处理因为后处理器在容器里是提前实例化的这个阶段你写的处理器可能还没注册到容器。这也是为什么有些扩展点必须等ApplicationContext刷新完成才能使用的原因。4.2BeanFactoryPostProcessor与BeanDefinitionRegistryPostProcessor的区别如果说BeanPostProcessor是对已经创建出来对象下手那BeanFactoryPostProcessor就是在对象还没创建时对 BeanDefinition 下手。它最大的价值在于你可以在容器实例化 Bean 之前动态修改某个 Bean 的定义比如替换实现类、修改属性值、增加 init 方法。例如PropertySourcesPlaceholderConfigurer就是BeanFactoryPostProcessor的实现它把配置文件里的${...}占位符替换成真实值。知不知道这个关系决定了你能不能理解为什么有些占位符在 XML 里没生效、而有的配置文件里却能生效。BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的子接口它在 BeanDefinition 注册之后、普通后处理器执行之前运行允许你动态注册新的 BeanDefinition。要动态给容器补充 Bean这个接口是你的必经之路。例如 MyBatis 的MapperScannerConfigurer就是靠它在容器里注册每一个 Mapper 接口的 BeanDefinition。我把两个扩展点的对比列一下方便挑选使用扩展点执行时机典型用途BeanFactoryPostProcessorBeanDefinition 加载后、Bean 实例化前修改配置信息、占位符替换BeanDefinitionRegistryPostProcessorBeanDefinition 注册阶段更早动态注册新的 BeanDefinitionBeanPostProcessorBean 初始化前后对 Bean 实例做增强、代理、替换4.3 事件机制从小循环依赖到业务解耦的优雅解法Spring 内置的ApplicationEventPublisher事件机制其实被低估了。很多业务上的“循环调用”问题用事件广播可以彻底绕开——A 完成操作后发布一个事件B 监听该事件再执行后续动作两者就不再直接引用对方循环依赖自然消失。这也是我在架构设计上经常推荐的方式。具体用法很简单实现ApplicationEventPublisherAware就能拿到发布器或直接注入ApplicationEventPublisher监听方使用EventListener注解标记方法事件类型决定了哪些监听器会被调用。如果你需要异步监听加上Async注解即可注意前面说的 AOP 失效场景确保监听器和事务方法不在同类内部调用。有一点要注意事件监听默认是同步执行的。发布事件相当于在调用链中插入监听器逻辑监听器抛出的异常会影响发布者的事务回滚吗默认情况下监听器异常会向外传播和发布者处于同一个事务上下文中如果不希望异常影响主流程监听器里要捕获处理或者为监听器配置自己的事务传播行为。Spring AI 这类新生态也是建立在同样的机制上的——Spring AI Alibaba、Spring AI Agent提供了很多自动配置的 Bean它们本质上都是通过 Spring 的扩展机制接入容器再由容器统一管理生命周期。学懂 Spring 的核心扩展机制再去看 AI 组件的接入原理会容易很多。5. 实战排查循环依赖报错、代理失效、Bean 作用域混乱5.1 循环依赖报错排查清单当启动日志出现The dependencies of some of the beans in the application context form a cycle时按下面的顺序排查能快很多。先看是不是 Spring Boot 2.6 之后的默认禁用了循环引用如果是老项目升级加一行spring.main.allow-circular-referencestrue先恢复运行。再看注入方式是构造器还是属性注入——构造器循环是 Spring 解决不了的要重构。再看 Bean 是不是原型作用域原型 Bean 根本不缓存循环依赖无法解决。最后查是不是Lazy没有用上Lazy注入可以打破循环因为它注入代理而不是直接创建依赖但它改变了 Bean 的初始化时机要评估是否符合预期。一个最容易忽略的多个模块之间通过Autowired产生隐式依赖链比如 A→B→C→A这种依赖链很长肉眼很难发现。可以在启动参数上加-Dspring.debugtrue打开 Debug 日志或者在 IDEA 的启动配置里开启 debug日志里会打印出完整的依赖链路径。5.2 代理失效排查小技巧用 Debug 输出判断代理类型当你在业务里发现Transactional没生效、或日志增强没执行时可以先在启动阶段打印当前 Bean 的类型Component public class ProxyCheckRunner implements ApplicationRunner { Override public void run(ApplicationArguments args) { Object bean SpringContextUtil.getBean(UserService.class); System.out.println(bean class bean.getClass()); System.out.println(is proxy (bean instanceof org.springframework.aop.framework.AopProxy)); } }输出如果是class com.xxx.UserService$$EnhancerBySpringCGLIB说明是 CGLIB 代理如果是jdk.proxy1.$Proxy说明是 JDK 动态代理如果直接是class com.xxx.UserService说明这个 Bean 没有被代理——那么 AOP 失效了检查切点表达式、方法权限、final 修饰符、同类调用这四个方向。还有一个实用技巧给目标方法打上断点看调用栈里是否有CglibAopProxy$DynamicAdvisedInterceptor或JdkDynamicAopProxy有就说明走了代理链没有就是直接调用原始方法——顺着调用栈往上翻就能快速定位到底是同类调用还是事件里直接 new 的对象。5.3 Bean 作用域混淆导致的状态异常单例 Bean 里注入原型 Bean是作用域问题的重灾区。比如一个单例的 Service 里注入原型组件PrototypeComponent容器在创建 Service 时只会注入一次原型 Bean后续每次调用 Service 里的业务方法拿到的都是同一个原型实例“原型”毫无意义。要真正按需获取新实例推荐用ObjectProviderTComponent public class SingletonService { Autowired private ObjectProviderPrototypeComponent provider; public void doSomething() { PrototypeComponent comp provider.getObject(); // 每次调用 getObject() 都会拿到一个新实例 } }ObjectProvider是 Spring 4.3 之后提供的注入方式它在延迟获取、可选依赖、多实例场景下特别好用。另外Scope(value prototype)在使用时也要留意如果原型 Bean 内部又注入了单例 Bean那还是共享的——作用域只在当前 Bean 的生命周期范围内生效不影响它依赖的其他 Bean。如果有人一直分不清作用域我通常用一句话总结单例是整个容器一份原型是每次获取一份请求作用域是一次 HTTP 请求一份。配合ObjectProvider原型注入的坑基本就能避开了。5.4 经验速查事务方法调用的正确打开方式我自己踩过最多坑的还是事务和 AOP 在同一类里互相调用。在这里直接给出一份速查清单都是实操能直接落地的做法场景推荐方案不推荐方案类内部方法需要事务TransactionTemplate手动管理this调用加注解同类内部方法需要 AOP 增强拆到独立 Service 类AopContext.currentProxy()自行转换异步方法需要事务独立 Bean TransactionalAsync同类自调用多数据源事务TransactionTemplate 显式指定事务管理器依赖默认事务管理器如果一定要在同类里走代理EnableAspectJAutoProxy(exposeProxy true)后通过((Service) AopContext.currentProxy()).method()调用是一种办法但它要求线程上下文里时刻有代理引用代码可读性差、性能也受影响只在不得已时再用。从设计角度讲把方法拆出去是更干净的选择。6. 从核心框架到 Spring Boot、Spring Cloud 与 Spring AI 的衔接6.1 Spring Boot 与 SSM 时代的本质差异很多人从 SSM 直接跳到 Spring Boot第一感觉是“配置少了一大堆”但少的是什么其实是 Spring Boot 把大量重复的配置工作收敛成了约定优于配置的自动装配。Spring Boot 的三大核心机制——EnableAutoConfiguration、ConditionalOnXxx条件注解、application.yml统一配置体系——都是在 Spring Framework 的BeanFactoryPostProcessor和BeanPostProcessor扩展点上构建出来的。比如EnableAutoConfiguration注解导入了AutoConfigurationImportSelector这个类会扫描spring.factories里注册的所有自动配置类再用条件注解逐个判断“这个配置要不要生效”。判断条件包括类路径上有没有某个类、容器里有没有某个 Bean、配置项是否等于某个值。这些逻辑实现都离不开BeanPostProcessor的思想——只是在更高的抽象层级上做批量决策。所以在学 Spring Boot 时不要再死记application.yml里的每项配置了更重要的是理解它背后对应的 Spring 机制。6.2 Spring Cloud 微服务与 Spring AI 新生态概览Spring Cloud Alibaba 是微服务场景下非常重要的技术栈它把 Nacos 注册中心、Sentinel 限流熔断、Seata 分布式事务等能力与 Spring 生态打通。在 Spring Cloud 体系里服务发现本质上是把 Nacos 的服务列表注入到 Spring 容器中Ribbon / LoadBalancer 负责负载均衡策略Feign 通过动态代理把 HTTP 调用封装成接口调用——这里面依然离不开 Spring 的动态代理机制。Spring AI 则是 2025 年热度极高的新方向比如 Spring AI Alibaba、Spring AI Agent、以及接入百炼 Qwen 模型的场景。它对已有 Spring 开发者来说最大的优势是沿用 Spring 的编程模型把模型调用、Prompt 模板、功能调用Function Calling、Agent 编排抽象成 Bean 和组件你不需要换一套框架就能把 AI 能力接入到已有系统里。餐饮 SaaS 集成 AI、OA 系统引入 AI 助手这类业务场景本质上都是把已有的 Spring Boot 服务通过 Spring AI 连接到大模型——核心仍然是 Spring 容器管理这些 AI 组件的生命周期、配置和依赖关系。反过来如果你 Spring 内核没搞懂遇到自动配置不生效、组件被代理两次、条件判断不命中这类问题排查起来会相当吃力。6.3 我建议的学习路线别急着追新先把内核钉牢现在的技术噪声很大从Spring AI 2.0到LangGraph4J再到各种 AI Agent 框架每天都有新东西。但在团队招聘和实际带人的过程中我发现一个规律Spring 内核掌握扎实的人学新框架特别快内核不牢的人追新框架永远在赶路。如果你现在刚开始系统性学习 Spring我的建议是先按这个路线走一遍第一理解 IOC 容器和 Bean 生命周期能画出完整时间线。第二理解三级缓存和循环依赖能解释为什么三级不是两级。第三理解 AOP 代理机制能用手写代码解释 JDK 代理和 CGLIB 的区别。第四理解BeanPostProcessor和BeanFactoryPostProcessor能自己写一个扩展点组件。第五动手用一个几百行的迷你版容器实现 Bean 注册、依赖注入和代理增强——哪怕只完成基础功能收获也远超预期。在此基础上再去接触 Spring Boot 自动配置、Spring Cloud Alibaba 微服务体系、Spring AI 等上层框架你会发现很多看过的源码逻辑都在“重复”因为你已经掌握了它们的共同底座。我个人在实际操作中的体会是Spring 这套设计最大的价值不只是让你能写出能跑的代码而是让你在遇到问题的时候多一个“分层排查”的视角。一个事务失效你能从代理链开始查一次启动失败你能从 BeanDefinition 加载顺序开始查一个神秘的对象不符合预期你能从缓存层级开始查。这种能力没法靠背面试题获得只能靠把核心机制之内化到骨子里。这篇“Spring 总结(上)”先把核心机制梳理清楚后续我会再写配套的实战篇把事务传播、多数据源处理、Spring Boot 自动装配源码、微服务治理这些内容一一铺开。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

MapStruct从入门到实战:@Mapper/@Mapping核心用法与踩坑总结 2026/10/1 16:36:34

MapStruct从入门到实战:@Mapper/@Mapping核心用法与踩坑总结

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
锐捷交换机配置避坑:VLAN、ACL、端口安全与链路聚合 2026/10/1 16:36:34

锐捷交换机配置避坑:VLAN、ACL、端口安全与链路聚合

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
WSL2图形化配置全指南:Ubuntu 22.04 + WSLg + VS Code深度集成 2026/10/1 16:36:34

WSL2图形化配置全指南:Ubuntu 22.04 + WSLg + VS Code深度集成

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Windows 安装 RabbitMQ 与 Erlang:版本匹配与启动排错 2026/10/1 16:36:33

Windows 安装 RabbitMQ 与 Erlang:版本匹配与启动排错

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Win10下JDK1.8+Eclipse环境部署实战手册 2026/10/1 16:36:33

Win10下JDK1.8+Eclipse环境部署实战手册

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
C++ Pimpl模式解析:从封装思想到编译优化实战 2026/10/1 16:36:26

C++ Pimpl模式解析:从封装思想到编译优化实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉