ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Spring源码深度解析:从IoC容器到AOP实现

Spring源码深度解析:从IoC容器到AOP实现 1. 为什么Spring源码值得你投入时间十年前我刚接触Spring时也曾被那些晦涩的源码吓退。直到在某次线上事故排查中被迫深入Spring事务源码才发现理解底层原理带来的技术自由度有多宝贵——那次我仅用20分钟就定位到其他团队三天都没解决的分布式事务问题。Spring作为Java生态的事实标准其设计思想影响着几乎所有主流框架。当你真正读懂Spring源码后会明显感受到三个层面的提升框架理解维度MyBatis的动态代理、Spring Security的过滤器链本质上都是Spring核心机制的延伸问题排查效率遇到诡异的事务失效、循环依赖问题时能直接从Bean生命周期入手分析架构设计能力Spring的扩展点设计、接口抽象策略都是可复用的架构范本2. 高效阅读源码的准备工作2.1 环境搭建实战建议使用IntelliJ IDEA Spring 5.3.x的组合当前最稳定的生产版本按这个步骤配置调试环境# 克隆Spring源码 git clone https://github.com/spring-projects/spring-framework.git git checkout v5.3.18 # 使用Gradle编译 ./gradlew :spring-oxm:compileTestJava关键技巧在build.gradle中添加compileJava.options.compilerArgs [-parameters]保留参数名信息为spring-context模块添加测试类作为入口点2.2 核心模块地图重点关注的5个核心模块spring-beansBean定义、依赖注入实现spring-context应用上下文、事件机制spring-aop动态代理、切面实现spring-tx事务管理抽象spring-webmvcDispatcherServlet实现提示初次阅读建议从DefaultListableBeanFactory开始这是整个IoC容器最核心的实现类3. IoC容器深度解析3.1 Bean生命周期全流程通过这个测试用例可以观察完整的Bean创建过程public class BeanLifecycleDemo { public static void main(String[] args) { AnnotationConfigApplicationContext context new AnnotationConfigApplicationContext(); context.register(MyConfig.class); context.refresh(); // 触发Bean初始化 context.close(); // 触发Bean销毁 } } Component class MyBean implements InitializingBean, DisposableBean { PostConstruct public void init() { System.out.println(PostConstruct); } Override public void afterPropertiesSet() { System.out.println(InitializingBean); } PreDestroy public void preDestroy() { System.out.println(PreDestroy); } Override public void destroy() { System.out.println(DisposableBean); } }执行后会按以下顺序输出构造器调用PostConstructInitializingBean.afterPropertiesSetPreDestroyDisposableBean.destroy3.2 循环依赖的解决之道Spring通过三级缓存巧妙解决了Setter注入的循环依赖问题缓存级别存储内容作用时机singletonObjects完整的Bean实例Bean初始化完成后earlySingletonObjects提前暴露的原始对象实例化后、属性填充前singletonFactoriesObjectFactory包装的工厂对象创建Bean实例时立即放入典型的问题排查场景// 构造器注入导致的循环依赖会直接报错 Component class ServiceA { private final ServiceB b; public ServiceA(ServiceB b) { this.b b; } } Component class ServiceB { private final ServiceA a; public ServiceB(ServiceA a) { this.a a; } }经验生产环境建议使用Lazy延迟加载解决特殊场景的循环依赖4. AOP实现原理剖析4.1 代理创建流程图解Spring动态代理的选择逻辑public class DefaultAopProxyFactory { public AopProxy createAopProxy(...) { if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces()) { return new CglibAopProxy(config); // CGLIB代理 } return new JdkDynamicAopProxy(config); // JDK动态代理 } }4.2 拦截器链执行机制关键源码在ReflectiveMethodInvocation.proceed()public Object proceed() throws Throwable { if (this.currentInterceptorIndex this.interceptorsAndDynamicMethodMatchers.size() - 1) { return invokeJoinpoint(); // 执行目标方法 } Object interceptor this.interceptorsAndDynamicMethodMatchers.get(this.currentInterceptorIndex); if (interceptor instanceof MethodInterceptor) { return ((MethodInterceptor) interceptor).invoke(this); } // 其他类型拦截器处理... }常见坑点同一个切面内Around会覆盖Before/After自调用方法不会触发AOP因为不走代理5. 事务管理底层实现5.1 事务传播行为对照表传播类型源码常量行为特征REQUIREDPROPAGATION_REQUIRED存在事务则加入否则新建REQUIRES_NEWPROPAGATION_REQUIRES_NEW总是新建独立事务NESTEDPROPAGATION_NESTED创建保存点实现嵌套事务NOT_SUPPORTEDPROPAGATION_NOT_SUPPORTED非事务方式运行5.2 事务失效的7种场景方法修饰符非publicSpring AOP无法代理private方法自调用问题类内部方法互相调用不会经过代理异常类型不匹配默认只回滚RuntimeException数据库引擎不支持MyISAM引擎无事务支持多数据源未指定需要Transactional(transactionManagerxxx)传播行为配置错误NOT_SUPPORTED会挂起已有事务try-catch吞异常未将异常重新抛出调试技巧在TransactionInterceptor.invoke()方法打条件断点观察事务开启/回滚逻辑6. Spring MVC核心流程6.1 DispatcherServlet处理时序HttpServlet.service()入口doDispatch()核心处理protected void doDispatch(HttpServletRequest request, HttpServletResponse response) { HandlerExecutionChain mappedHandler getHandler(request); HandlerAdapter ha getHandlerAdapter(mappedHandler.getHandler()); // 拦截器preHandle if (!mappedHandler.applyPreHandle(request, response)) return; // 实际处理器执行 ModelAndView mv ha.handle(request, response, mappedHandler.getHandler()); // 渲染视图 applyDefaultViewName(request, mv); mappedHandler.applyPostHandle(request, response, mv); }6.2 参数解析器妙用自定义参数解析器示例public class JwtUserArgumentResolver implements HandlerMethodArgumentResolver { Override public boolean supportsParameter(MethodParameter parameter) { return parameter.getParameterType().equals(User.class); } Override public Object resolveArgument(...) { String token request.getHeader(Authorization); return JwtUtils.parseUser(token); // 实际项目需要处理各种异常情况 } } // 注册配置 Configuration class WebConfig implements WebMvcConfigurer { Override public void addArgumentResolvers(ListHandlerMethodArgumentResolver resolvers) { resolvers.add(new JwtUserArgumentResolver()); } }7. 源码阅读进阶技巧图形化工具辅助使用IDEA的Diagrams功能生成类图SequenceDiagram插件绘制调用时序关键断点设置AbstractAutowireCapableBeanFactory.doCreateBean()JdkDynamicAopProxy.invoke()TransactionInterceptor.invoke()改造源码实践 在本地分支尝试修改Spring源码给BeanDefinition添加自定义属性实现新的Scope类型扩展AOP通知类型我在团队内部推行源码阅读的实践经验是每周组织一次源码快闪活动每人用5分钟分享一个Spring类的设计亮点。这种方式既能保持持续学习又不会给团队带来太大负担。建议从DefaultSingletonBeanRegistry这个相对独立的类开始尝试这种分享模式。
返回列表