新闻详情

新闻详情

首页 / 资讯中心 / 详情

Spring IOC与AOP原理深度剖析:从Bean生命周期到动态代理

发布时间:2026/9/7 23:55:03来源:尧图网络
Spring IOC与AOP原理深度剖析:从Bean生命周期到动态代理
面试聊到 Spring十个有九个会问 IOC 和 AOP。你要是只背“控制反转”、“面向切面”这两个词基本等于没答。我这些年面试别人最怕听到的就是这种背概念的回答——一听就是没自己翻过源码、没踩过坑的。今天这篇我尽量把 Spring 的 IOC 和 AOP 原理讲透从设计思想到源码关键点再到日常开发里的实际应用和失效场景一次性掰开揉碎。不管是准备面试还是想真正搞懂框架这篇都值得你花十分钟仔细看。1. 为什么面试官揪着 IOC 和 AOP 不放先说个扎心的事实很多人 Spring 用了好几年Autowired 和 Transactional 天天写但真要他说清楚这两个注解背后干了什么往往就卡住了。这不是个别现象而是因为 Spring 的 IOC 和 AOP 藏得太深——你在业务代码里根本感觉不到它们的存在越感受不到越说明它们把解耦这件事做到了极致。面试官问 IOC 和 AOP其实不是在考你两个名词解释而是在考察三件事第一你有没有对象管理意识。一个系统几百个类互相 new 来 new 去改一个构造方法全盘崩溃这种痛苦谁经历谁知道。IOC 解决的就是这个痛让对象不自己找依赖而是等着容器把依赖送上门。第二你有没有横切逻辑的抽象能力。日志、事务、权限、限流这些逻辑散落在每个业务方法里如果你只会复制粘贴那说明你的抽象层级还停留在“能跑就行”。AOP 解决的正是这种横切关注点的收敛问题。第三你有没有翻过源码的耐心。原理这东西光看文档永远只能懂个皮毛敢往下钻到代理生成、Bean 生命周期这一层才叫真的懂。所以这篇文章不是给你背的是给你“通”的。我会把 IOC 从 BeanDefinition 加载到单例池填充的完整链路讲清楚把 AOP 从 JDK 代理和 CGLIB 的底层差异到拦截器链的执行顺序讲清楚。顺带把我这些年用 Spring 踩过的坑和面试时最常追问的问题一并交代了绝对比你在网上零散搜到的要系统。2. IOC 原理拆解从“找对象”到“被投喂”2.1 控制反转到底反转了什么很多人把 IOC 理解成“用 Spring 就不需要 new 对象了”这话对但没说到根上。IOC 反转的不是“创建对象”这个动作而是“控制权”——谁来决定一个对象用哪个实现、什么时候创建、创建完交给谁。拿一个订单服务举例子。不用 Spring 的时候你写 OrderService里面要用 UserService你得自己new UserServiceImpl()写着写着觉得 UserService 应该加个缓存代理你得改 OrderService 的代码后来发现 UserService 构造方法多了个参数你还要再改一遍。一个类这么改还能忍一百个类依赖它那就是灾难。传统开发里对象之间的依赖关系是由每个类自己维护的这就叫控制权在“使用方”手里。IOC 出现之后你把所有类的依赖关系写进配置或者注解里容器统一负责创建和组装。OrderService 不再管 UserService 怎么来的它只需要在构造方法或者字段上声明“我需要一个 UserService”容器在合适的时机把实例给它送过去。反转的本质是依赖关系管理权的转移。打个比方以前你自己买菜、洗菜、炒菜、洗碗所有流程自己控制用了 IOC你只需要坐在桌前说“我要一份鱼香肉丝”后厨容器会按菜单把菜做好端上来你根本不需要关心鱼香肉丝是哪个厨师做的、锅是哪个牌子的。这种模式最大的好处是只要你换一份菜单改配置后厨就能换一套做法而你的“吃菜”逻辑一行都不用动。2.2 BeanDefinition 与注册中心IOC 的第一块地基IOC 容器不是上来就 new 对象它首先得知道“有哪些类需要管理、怎么实例化、依赖什么”。这些元信息在 Spring 里被抽象成 BeanDefinition。你写的bean iduserService classcom.example.UserServiceImpl/或者ComponentAutowired最终都会被解析成 BeanDefinition 对象里面记录了类名、作用域、懒加载标志、初始化方法、属性值等一堆元数据。整个流程大致是这样的Spring 启动的时候XmlBeanDefinitionReader 或 ConfigurationClassPostProcessor 等 BeanDefinitionReader 会把 XML、注解、Bean 方法统统解析成 BeanDefinition然后注册到 DefaultListableBeanFactory 里的一个 ConcurrentHashMap 中。这个 Map 就是容器的“注册中心”key 是 beanNamevalue 是 BeanDefinition。你可以把 BeanDefinition 理解成一张“零件图纸”图纸先入库后续要生产零件创建 bean的时候工厂拿着图纸照方抓药。这里有个我建议你重点理解的细节Spring 的扩展点很多都发生在 BeanDefinition 阶段。比如你实现了 BeanDefinitionRegistryPostProcessor可以在所有 bean 实例化之前往容器里动态注册新的 BeanDefinition甚至可以篡改已有 BeanDefinition 的属性值。很多框架的自动配置就是这么干的——先通过条件判断决定要不要注册某个类再通过 BeanDefinition 的属性设置来完成默认配置。你要是搞懂了这一层看 Spring Boot 自动配置的源码会轻松很多。2.3 Bean 生命周期一个对象在容器里的完整一生BeanDefinition 拿到手之后容器就开始了 Bean 的创建链路。很多人背生命周期背得头疼其实就是因为没有把这条链路和源码对上号。我来按源码的 Actual 执行顺序讲第一步实例化Instantiation。容器通过构造方法反射创建对象。默认使用无参构造器如果你写了 Autowired 在构造方法上Spring 会把你需要的依赖作为参数传递进去再创建。这里要留意实例化出来的对象还只是个“半成品”属性全是 null。第二步属性填充Populate。这就是 Autowired、Resource、Value 发挥作用的地方。Spring 会遍历 BeanDefinition 里的 PropertyValues逐个解析依赖并注入。注入方式有三种构造器注入、Setter 注入、字段注入。我自己在项目里基本只用构造器注入因为能保证对象在创建后就是一个完整可用的状态不会出现字段为 null 的中间态字段注入写起来最省事但最不推荐测试的时候 mock 都麻烦。第三步Aware 回调。如果 Bean 实现了 BeanNameAware、BeanFactoryAware、ApplicationContextAwareSpring 会在这里回调对应方法把名称、容器等资源塞给你。早期的 Spring 代码经常这么干现在大部分场景用 Autowired 注入 ApplicationContext 也差不多但 Aware 是回调机制、走的更底层在某些特殊场合比注解更好使。第四步BeanPostProcessor 的 postProcessBeforeInitialization。这一步是扩展重地Spring 自己很多功能就挂在这里比如 Autowired 的 AutowiredAnnotationBeanPostProcessor它会在这一步把依赖解析出来并注入到字段里。第五步InitializingBean 和 PostConstruct。InitializingBean 是接口里面有 afterPropertiesSet 方法PostConstruct 是注解。spring 会先执行 InitializingBean 的 afterPropertiesSet再执行 PostConstruct这个顺序要注意实际是先 BeanPostProcessor 的 Before 方法然后 InitializingBean.afterPropertiesSet再 PostConstruct不对顺序有差异。我直接说结论PostConstruct 基于 CommonAnnotationBeanPostProcessor 这个 BeanPostProcessor它在 postProcessBeforeInitialization 阶段调用InitializingBean 的 afterPropertiesSet 是在 invokeInitMethods 里调用。所以实际顺序是PostConstruct 先执行在 Before 阶段然后 InitializingBean 执行最后是自定义 init-method。这个顺序面试偶尔会考你记住“注解优先、接口次之、XML 最后”这个口诀就行。第六步BeanPostProcessor 的 postProcessAfterInitialization。这一步完成后对象就进入“可用状态”了。AOP 的代理对象就是在这个阶段生成的——如果当前 bean 命中了切点Spring 就会返回一个代理对象而不是原始对象。这一点特别重要很多人以为 AOP 是创建完对象再包一层其实它是在生命周期这一步直接“偷梁换柱”。第七步容器关闭阶段触发 PreDestroy 和 DisposableBean.destroy。这就不细说了你了解单例 bean 的销毁回调顺序即可。2.4 依赖注入的两种形态byType 与 byNameAutowired 默认是 byType先按类型找找到多个再按字段名匹配还不行就报 NoUniqueBeanDefinitionException。Resource 是 Java 标准注解默认按 byName匹配不到再退级 to byType。这两种策略日常用着没区别但你如果定义了一个接口两个实现类又没有标注 Primary 或 Qualifier那 Autowired 一定会报错。这时候你有几个选择在其中一个实现类上加 Primary 表示优先或者在注入点加 Qualifier(具体Bean名)精确指定。我建议把 Primary 留给“默认实现”的场景而 Qualifier 用在“这个注入点特别指定用哪个实现”的场景两者语义不一样。还有一个特别容易忽略的知识点构造器注入在 Spring 4.3 以后如果类只有一个构造器可以省略 AutowiredSpring 会直接用这个构造器。如果你把这个特性结合 Lombok 的 RequiredArgsConstructorController 里的依赖注入会干净到不可思议。构造函数注入还有个好处是方便做单元测试不依赖 Spring 容器也能手动 new 出来。2.5 三级缓存与循环依赖Spring 最精妙的设计Spring 有一个单例 bean 的三级缓存解决了循环依赖的问题名字很直白singletonObjects一级缓存、earlySingletonObjects二级缓存、singletonFactories三级缓存。三个 Map 各司其职一级缓存存成品 bean就是完全初始化好的对象三级缓存存 ObjectFactory也就是一个能产出早期对象的工厂二级缓存存早期暴露的半成品 bean用于“提前拿出来注入给别人”。整个过程我拿 A 依赖 B、B 依赖 A 来举例Spring 创建 A实例化完成后发现 A 还没有被别的 bean 引用于是把 A 的工厂ObjectFactory放进三级缓存。开始填充 A 的属性发现需要 B。去创建 B同样实例化后把 B 的工厂放进三级缓存。填充 B 的属性时发现需要 A。这时候从三级缓存里取出 A 的 ObjectFactory 执行 getObject()拿到一个早期暴露的 A 的引用如果是代理配置这里返回的就是提前暴露的代理对象把这个引用放到二级缓存同时删掉三级缓存里的工厂。B 拿到 A 的引用属性填充完成B 初始化完成进入一级缓存。回到 A 的创建过程继续完成属性填充和初始化最后也进入一级缓存。看到关键点了三级缓存里存的是工厂不是直接存对象。为什么要多一层因为 AOP 代理可能要在对象实例化后就生成才能被循环依赖里的对方拿到代理。如果二级缓存直接存原始对象代理生成时机就晚了。三级缓存的存在就是为了“延迟代理生成”但又保证循环依赖出现时能拿到一个“对的”对象。Spring 的解决思路不是消灭循环依赖而是在“允许循环”和“接口完整”之间找到一个平衡点。但是注意循环依赖不是所有场景都能解决。构造器注入的循环依赖是无解的因为对象都还没实例化出来没法提前暴露prototype 作用域的 bean 也不解决循环依赖因为原型模式每次都要新建还有 Async 这种代理增强也可能导致循环依赖问题因为代理生成时机被推后了。这些都没法用三级缓存解决硬写出来就是启动报错。所以规范上我是明令项目里不允许写循环依赖的宁可多写一层中间层也不要让两个 service 互相指。3. AOP 原理拆解横切逻辑的收割机3.1 AOP 到底解决什么问题如果你从来没接手过那种几千行的老 Service 类你可能对 AOP 的价值感受不深。我在老项目里见过一个转账方法里面夹杂着开启事务、记操作日志、检查权限、发送通知、计算耗时……每种逻辑占几行互相穿插谁改谁崩溃。AOP 的思路很简单把这类横切逻辑事务、日志、权限、限流从业务代码里抽出来独立成“切面”在合适的时机以“拦截”的方式织入业务方法。业务代码只需要关心自己的核心逻辑其他事情交给切面去管。这样做的好处是不侵入业务代码二次维护时只改切面类就行。3.2 代理模式AOP 的地基Spring AOP 是跑在代理模式之上的。它不修改目标类的字节码而是生成一个代理类来控制对目标对象的访问。代理类里包含一组拦截器链增强逻辑调用目标方法之前先经过这些拦截器拦截器可以做前置处理、后置处理、甚至完全替代目标方法的执行。这里有两条技术路线JDK 动态代理要求目标类必须实现至少一个接口。代理类由 JVM 在运行时生成它实现同样的接口在调用接口方法时通过 InvocationHandler 来转发。生成速度快但只支持接口方法。CGLIB基于字节码生成技术直接在运行时生成目标类的子类重写非 final 的方法来实现方法拦截。不要求目标类有接口但目标方法不能是 final 或 static否则无法被重写。Spring 的默认策略是如果目标类实现了接口优先用 JDK 动态代理否则用 CGLIB。不过从 Spring Boot 2.x 开始官方把 spring.aop.proxy-target-class 默认设置为 true也就是说默认强制使用 CGLIB不再看有没有接口。这个变化背后有个实际原因很多人写的 Service 类只标注了 Service没刻意去抽接口用 JDK 动态代理反而会因为类型不匹配而注入失败。强制 CGLIB 之后所有 bean 都能被代理省得踩坑。3.3 五大通知类型与拦截器链的执行顺序Spring AOP 定义的“通知Advice”就是你要在目标方法前后执行的那段逻辑。常用的是这五种Before方法执行前。AfterReturning方法正常返回后。AfterThrowing方法抛出异常后。After方法结束后不管正常返回还是抛异常都会走这里类似于 finally。Around能够包裹整个方法调用前后都拦截甚至可以修改返回值、捕获异常、重新执行目标方法。面试时经常追问的一个点这些通知的执行顺序。我直接给你结论以正常返回为例Around 的 proceed 之前 → Before → 目标方法执行 → Around 的 proceed 之后 → AfterReturning → After。如果方法抛异常Around 的 proceed 之前 → Before → 目标方法抛异常 → Around 捕获异常如果没吞掉→ AfterThrowing → After。这里有一个很容易踩的坑AfterReturning 和 After 的执行顺序。很多人以为是 After 先执行实际是 AfterReturning 或 AfterThrowing 先执行After 最后执行。Spring 的拦截器链执行逻辑是递归式的先进入第一个拦截器处理后调用下一个最内层是目标方法然后从内向外返回。所以“后执行”的通知优先级反而“更靠前”这里需要你在代码里多写几个切面验证一下才会印象深刻。3.4 切点表达式怎么告诉 Spring 该拦谁AOP 不好用的第一大原因就是切点表达式写不对。表达式用 execution 和注解两种主流方式。execution 写法常见的是execution(* com.example.service.*.*(..))拆开看第一个 * 表示返回类型任意com.example.service.* 表示这个包下所有类第二个 * 表示所有方法(..) 表示参数任意。但要注意这个写法只能拦到指定包下的类直接定义的方法不能拦截子包或者接口实现类里多出来的方法。我实际用得更多的是注解式切点比如自定义一个 RateLimit 注解然后写Around(annotation(rateLimit)) public Object rateLimit(ProceedingJoinPoint pjp, RateLimit rateLimit) throws Throwable { // ... }这种方式的好处是业务方可以用注解精确控制哪些接口需要限流、什么阈值、什么速率不需要关心包路径维护起来非常直观。像我之前做接口限流就是基于注解的 AOP 切面实现的从 Redis 里用 Lua 脚本做滑动窗口计数超过阈值直接抛异常业务代码一行都不用改。3.5 从源码看 AOP 的织入时机我之前说 AOP 代理对象是在 BeanPostProcessor 的 postProcessAfterInitialization 阶段生成的这里具体展开一下。Spring 容器在创建每个 bean 的过程中都会调一遍所有注册的 BeanPostProcessor。其中有一个叫 AbstractAutoProxyCreator 的处理器它的 postProcessAfterInitialization 方法里会去检查“当前 bean 是否匹配已有的 Advisor切面”。这个检查基于你配置的切点表达式和当前 bean 的类信息做匹配匹配成功则调用 createProxy 方法生成代理对象。createProxy 内部做的事情是确定用哪种代理方式JDK 还是 CGLIB→ 创建代理工厂 → 获取所有适用于当前 bean 的通知器 → 组装成拦截器链 → 交给 ProxyFactory 生成代理。所以整个链路就是Bean 创建 → 属性填充 → 初始化 → BeanPostProcessor 检查切点 → 命中 → 生成代理 → Bean 的使用者拿到的其实是代理。这也是为什么 AOP 对调用方是透明的Controller 里 Autowired 注入的 Service 类型是原始类型还是代理类型你编译期看不出来但运行期大多数情况下你会拿到一个 CGLIB 子类或者 JDK 代理。你在 IDE 的调试窗口能看到它的 toString 输出是com.example.service.UserServiceImpl$$EnhancerBySpringCGLIB$$xxxx看到这个基本就能确认 AOP 生效了。3.6 事务注解 Transactional 也是 AOP你用 Transactional 开启事务底层就是 Spring 的事务 AOP 切面在工作。它本身定义了一个 Advisor其中 Pointcut 匹配所有标注了 Transactional 的方法Advice 就是 TransactionInterceptor。这个拦截器的逻辑是什么呢方法执行前根据事务配置开启一个数据库连接设置自动提交为 false执行目标方法方法成功提交事务方法抛出 RuntimeException 或 Error回滚事务如果抛出的是 checked exception默认不回滚——这是个经典的坑。如果你想让 checked exception 也回滚要写Transactional(rollbackFor Exception.class)。另外事务的传播行为也是这个拦截器里实现的。REQUIRED 是默认的当前有事务就加入没有就新建。REQUIRES_NEW 是挂起当前事务创建一个新事务。这个在自调用场景下特别容易失效下文我会专门讲。4. 那些年我们踩过的坑高频问题排查实录4.1 为什么同一个类里的方法调用AOP 不生效这个是面试题里出现频率最高的坑。你写了一个 ServiceService public class OrderService { Transactional public void create(Order order) { // ... updateStock(order.getProductId()); } Transactional(propagation Propagation.REQUIRES_NEW) public void updateStock(Long productId) { // ... } }你在 create 里调 updateStockupdateStock 加的事务传播是 REQUIRES_NEW你期望它开一个新事务。但实际上事务根本没生效或者回滚行为不符合预期。原因是 AOP 代理对象是在容器外部注入时生效的但同一个类内部的方法调用走的是 this 引用根本不会经过代理对象。也就是说 updateStock 被调用时代理的拦截器链压根没机会执行事务自然就没开。解决方式有三种把 updateStock 拆到另一个 Service 里通过注入的新 Service 调用或者通过 AopContext.currentProxy() 拿到当前代理对象再调用或者在同类里注入自己Spring 支持自注入不过看着有点绕。这个问题背后其实是 Spring AOP 基于代理的一个天然限制代理只能拦“外部调用”拦不住“内部调用”。理解了这一点你就知道为什么很多框架要求你事务方法不能写在被调用的内部方法上。4.2 为什么 JDK 动态代理下注入代理对象会失败Spring Boot 2.x 默认强制 CGLIB 之后这个问题少了很多但老项目里你照样会遇到。假设你的 UserService 有接口 UserServiceInterface且开启了 JDK 动态代理proxy-target-classfalse那么容器里注入的是$Proxy类型的代理对象。如果你在 Controller 里这样写Autowired private UserServiceImpl userService;运行时大概率报 NoUniqueBeanDefinitionException 或者类型不匹配的异常。因为 UserServiceImpl 是一个具体类容器里真正注册的是 UserServiceInterface 类型的代理按照类型去匹配 UserServiceImpl 是找不到的。解决方法是改为注入接口类型Autowired private UserServiceInterface userService;这么多年踩下来我个人的建议是如果你的 Service 没有明确的接口抽象需求比如有多个实现就别硬造接口了直接用类加 CGLIB 代理代码干净很多。如果确实需要接口那把 Spring Boot 的默认配置保持住别手动去关 CGLIB。4.3 为什么 Async 在同类里调用也不生效Async 的失效原理和事务失效是一样的代理拦截。你写了一个异步方法然后从同类里的另一个方法直接调它其实是 this 调用代理不拦截异步就静默退化成同步执行。表面上看代码没问题功能在跑但接口响应时间不会缩短日志里也没有异常排查起来特别让人抓狂。解决方法和事务一样把异步方法放到另一个单独的类里或者通过代理调用。再补一句Async 的切面处理依赖线程池Spring Boot 默认的线程池不大高并发场景一定要定义自己的 TaskExecutor 并把 Async 的 value 指定过去不然并发一上来线程池排队异步反而拖垮系统。4.4 Spring Boot 报循环依赖错误怎么定位Spring Boot 2.6 默认禁止了循环依赖把 allow-circular-references 设为 false。所以项目升级时经常出现这种报错The dependencies of some of the beans in the application context form a cycle定位方法就一个核心思路看启动日志里打印的循环链找到 A → B → C → A 这条线然后打破其中一个交叉点。常见做法是把互相依赖的公共逻辑抽到新类里或者把其中一边改成通过事件通知机制解耦ApplicationEventPublisher或者让其中一边通过 ObjectProvider 延迟获取依赖。循环依赖本质上说明你的类职责划分有问题不是 Spring 不让你写是代码的味道不对。我建议这种问题要从设计上去改而不是通过配置把开关打开。4.5 定义切面时容易犯的几个低级错误切面不生效90% 的原因归结为这几种切点表达式写错了匹配不到任何方法。你可以在启动日志里加上EnableAspectJAutoProxy然后打开 debug 级别日志观察匹配结果。切面类没被 Spring 管理。AOP 切面本身必须是一个 bean你写了个普通类忘了加 Component 或 Aspect自然不会被织入。多个切面优先级不清。加了 Order(1) 和 Order(2) 之后执行顺序有讲究。Order 的值越小优先级越高但要注意 Around 的前置逻辑是值小的先执行而后置逻辑是值小的后执行。和拦截器链的方向一致。方法被 final 修饰。CGLIB 无法重写 final 方法切点在这个方法上失效且不会有任何报错。我排查过几次“为什么接口明明配了限流却没有生效”一查是方法加了 final这种问题工具类还看不出来只能靠经验。4.6 如何快速判断当前注入的对象是不是代理开发环境调试的时候经常需要确认一个 bean 是否被代理了以及是 JDK 代理还是 CGLIB 代理。最简单粗暴的方式是在 IDE 的调试窗口里看变量的类名JDK 代理通常是$Proxy123CGLIB 代理是Class$$EnhancerBySpringCGLIB$$xxxx。代码里也可以用工具判断AopUtils.isAopProxy(userService) AopUtils.isJdkDynamicProxy(userService) AopUtils.isCglibProxy(userService)如果你在测试某个 AOP 特性死活不生效先跑一下这几个判断看看是不是根本没走到代理路上。这种“确认现状”的排查习惯能帮你节省大量时间。4.7 基于注解的接口限流 AOP 实战思路既然前面提到限流我把这个案例说完整一点它是最能体现 AOP 价值的小项目之一。思路是自定义一个 RateLimit 注解定义在 Controller 方法上支持设置 QPS 阈值和时间窗口。限流规则存储到 Redis 里用 Lua 脚本来做滑动窗口计数保证原子性。然后写一个 Aspect 切面Around(annotation(rateLimit))在方法执行前先去 Redis 校验当前窗口内已经有多少请求如果超过阈值直接抛自定义异常或者返回一个统一错误体。这样一个切面写完之后项目里所有接口只要标注注解就能限流不用每个 Controller 里都写一遍判断逻辑。你后续想改成令牌桶算法只需要改切面里的算法实现业务代码一行不动。这就是 AOP 的典型收益——横切逻辑收敛在一个地方业务代码保持干净。更进一步的你可以把不同接口的限流阈值放在配置中心切面里动态读取这样调整限流策略连发布都不用发。这个方案我在生产环境跑过稳定性和扩展性都在线。需要注意的点是Redis 调用本身有网络开销如果限流是核心诉求建议在本地做一个前置计数降低 Redis 压力还有 Lua 脚本要避免拼接用户输入防止注入风险。5. 手写一个迷你 Spring彻底搞懂 IOC 和 AOP很多次面试里追问到最后面试官都会来一句“如果让你手写一个简化版 Spring你会怎么设计”这不是让你真的去写几万行代码而是考你对核心机制的理解深度。我在这里把设计思路讲一下照着这个思路你自己也能用几百行 Java 搭出一个可运行的迷你容器。IOC 部分你需要四个核心组件一个 BeanDefinition 类记录类名、作用域、属性依赖列表一个注册表用 ConcurrentHashMap 存 beanName → BeanDefinition一个创建器通过反射实例化对象并填充属性一个单例缓存存已经创建好的成品对象。创建过程就是扫描包 → 识别 Component 注解生成 BeanDefinition → 注册 → 按需创建 → 属性填充 → 缓存。AOP 部分你需要一个动态代理工厂JDK 或 CGLIB一个切面定义类记录切点表达式和通知逻辑一个代理创建器在 bean 初始化完成后检查方法是否命中切点命中则生成代理。关键就是在创建器的最后一步判断要不要返回代理对象。你要是能自己把这个迷你框架写出来Spring 的底层原理你已经掌握七成了。剩下的就是那些 BeanPostProcessor、BeanFactoryPostProcessor 的扩展逻辑在迷你版本里对应的是“每个 bean 创建前后执行哪些回调”理解了扩展点机制看任何 Spring 高级特性都不再发怵。根据我的经验很多人学 Spring 老觉得源码读不进去根源就是把阅读顺序搞反了。先看大流程再看扩展点最后再去抠某一个具体类的细节。比如你想搞懂事务先看 TransactionInterceptor 里 200 行核心逻辑就够了别一开始就去翻 PlatformTransactionManager 的十几个实现类。抓主干放枝叶这是读源码最快的方式。最后说句实在话IOC 和 AOP 看起来是两个知识点其实是一件事的两面。IOC 让对象之间的关系交给容器管理AOP 让横切逻辑在容器管理对象的边界上无限发挥。你理解了“容器接管对象的整个生命周期”再看事务、缓存、切面、异步这些高级功能本质上都是生命周期里挂载的处理器。框架再迭代核心思想就这几条把这个根基打牢Spring Boot、Spring Cloud 换着花样出现你也不会慌。实战中我个人的体会是不要为了用 AOP 而用 AOP但一旦识别出“这个逻辑横切了很多业务方法且会频繁变化”AOP 几乎是最优解。从最基础的单点登录校验到分布式系统的全链路日志 TraceId 注入再到生产环境的接口限流与熔断降级全都可以用 AOP 这一套思路去实现。每次写完一个切面看到业务代码仍然干净清爽你就会理解 Spring 为什么能统治 Java 生态这么多年——它让复杂系统的开发变得像搭积木一样清晰而有序。
网站建设高端定制企业官网
RELATED

相关资讯

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

较早相关资讯

最新相关资讯

企业私有化部署AI Agent:从模型到业务系统的完整工程链路 2026/9/8 2:52:31

企业私有化部署AI Agent:从模型到业务系统的完整工程链路

真正决定企业私有化部署 AI Agent 成败的,往往不是模型本身,而是从模型到业务系统之间的那条工程链路。KylinWork 这类企业级 Agent 平台被反复讨论,原因也在于它把单个智能体功能拉宽成了一个可落地的系统:模型推理、智能体编排、…

阅读更多 →
用Qt写飞机大战:图形视图框架与性能优化的实战复盘 2026/9/8 2:52:31

用Qt写飞机大战:图形视图框架与性能优化的实战复盘

简介:Qt飞机大战游戏.zip是一份基于Qt框架开发的经典空战游戏完整工程,面向正在学习Qt/C的开发者与游戏编程入门者,可帮助理解跨平台GUI应用中的2D绘图、事件处理、信号槽通信等核心机制。包内共223个文件,以105个头文件和32个C源…

阅读更多 →
从Demo到生产:智能体可审计工程闭环的完整路径 2026/9/8 2:52:31

从Demo到生产:智能体可审计工程闭环的完整路径

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

阅读更多 →
电商Agent架构设计与生产实践:从工具调用到安全兜底 2026/9/8 2:52:31

电商Agent架构设计与生产实践:从工具调用到安全兜底

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

阅读更多 →
YU12/I420/IYUV命名纠葛:YUV420P内存布局与实战解析 2026/9/8 2:52:31

YU12/I420/IYUV命名纠葛:YUV420P内存布局与实战解析

1. 一个误导了很多人的命名纠葛 1.1 我亲身经历的一次"彩屏"事故 早几年做视频采集模块时,遇到过一件让我印象深刻的事。设备端编码器输出的数据,SDK文档上清清楚楚写着"IYUV",我按I420的布局去解析,结果画面…

阅读更多 →
Word页眉页脚设置全攻略:分节符、页码域与论文排版实战 2026/9/8 2:49:30

Word页眉页脚设置全攻略:分节符、页码域与论文排版实战

许多人在写论文、做标书或整理正式报告时,最头疼的往往不是正文内容,而是页眉页脚。页码从正文开始算、每一章页眉不同、双面打印时奇偶页页眉不一样、页眉默认带一条横线怎么去掉……这些需求看起来简单,但真在 Word 里操作起来,…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

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

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