手撕spring核心源码,彻底搞懂spring流程

简介: 手撕spring核心源码,彻底搞懂spring流程

引子

 


十几年前,刚工作不久的程序员还能过着很轻松的日子。记得那时候公司里有些开发和测试的女孩子,经常有问题解决不了的,不管什么领域的问题找到我,我都能帮她们解决。但是那时候我没有主动学习技术的意识,只是满足于解决问题,错过了能力提升最好的阶段。

 

老公是个截然相反的类型,我就看他天天在宿舍里学习。学来学去也就那样了。他不陪我玩,我虽然心里不乐意,但是还好那时候未卜先知:知道自己能生一个长的帅、和我兴趣相投、天天粘我,我在他旁边他睡觉都能笑出声的儿子,就忍下了。我就自己学学历史、文学,出去上个外语培训班,什么感兴趣学什么。就是正经工作中的东西不学。

 

后来才发现自己犯了多大一个错误:没有尽早建立一个系统性的知识体系和思维,还影响了思维上的连续性和逻辑性。武侠故事里一夜得到神功是骗人的,功夫还在平时。老公看似做了十几年长进不大的事,基础却非常扎实,有功力。但是如果他能头脑再活络些,掌握要领,可以事半功倍。

 

很多朋友想学习Spring,总是很快地从入门到放弃。原因是头脑中没有框架,学到的东西没有索引,最终被深藏于记忆深处。本文通过手撕spring核心源码的方式,旨在让大家可以在头脑中形成一个spring框架,然后自己在工作中可以带着问题翻看源码,给这个框架添枝加叶,最终形成了一个饱满的spring知识体系。

 

本文整体采用由浅入深的逻辑结构。侧重于面向于平时工作或学习中用spring写过业务代码的朋友。

 

HelloWorld版本Spring启动代码

 


有些朋友可能写过或者见过ClassPathXmlApplicationContext、AnnotationConfigApplicationContext,它们都是常见的spring容器或者是上下文。既然说到这里,就先来解释什么是spring容器,什么是spring上下文。

 

Spring的核心就是容器,负责对象的整个生命周期:创建、管理、销毁程序执行过程中需要的对象。

 

Spring容器又分为两种类型:第一种是BeanFactory,最简单的容器,只能提供基本的DI功能)另一种是:继承了BeanFactory后派生而来的ApplicationContext,被称之为Spring上下文。能提供更多企业级的服务。咱们最常用的还是基于IOC(控制反转)的上下文容器。

 

现在SpringBoot使用的很多,所以今天咱们手撕的主要是实现 AnnotationConfigApplicationContext 这个注解上下文的原理。

 

咱们开始手撕。参照下面咱们用 spring 的方法,首先要有一个上下文将配置参数传入,然后有一个 getBean 可以获取对象来使用。


1112728-20220404142813322-978158074.png


那咱们就根据这两个特质自己写一个类,要点如下图1和2两步。


1112728-20220404142829402-1289067167.png


既然需要将configClass配置类作为参数传入,就先来构造一个空的配置类。


1112728-20220404142845427-1900612528.png


1112728-20220404142845427-1900612528.png这样从表面上,就可以像使用AnnotationConfigApplicationContext一样使用Spring容器了。


1112728-20220404142908498-1958613893.png


为了标注重点和屏蔽用户终端差异带来的文章阅读体验差异,这里我直接用的截图。代码文字在 https://github.com/xiexiaojing/yuna 里可以找到。


手撕Spring扫描流程

 


所谓HelloWorld版本就是除了入门,其他没有任何作用。咱们希望它可以实现自动扫描文件夹下的带有@Component注解的Bean完成注入。首先自己来新建一个@Component注解:


1112728-20220404142936052-1957691123.png


并在UserService中使用这个注解:


1112728-20220404142955353-1309029082.png


当然,Component注解是用来扫描的,那还得来定义一个扫描注解,就是把Component注解复制一份,改个名字:


1112728-20220404143012001-1664607125.png


ComponentScan这个注解要加在YunaConfig上,通过它来加载扫描配置:


1112728-20220404143053087-1224650099.png


咱们再回到YunaApplicationContext,配置类定义完之后,咱们下一步上下文就来解析它:


1112728-20220404143033589-1009692076.png


这时候咱们回到main方法,运行结果:


1112728-20220404143126702-2076581306.png


咱们拿到了解析路径,但是UserService这个Bean还是空的。重点来了,下面的代码要注意看:


1112728-20220404143146819-19719912.png


上面首先使用hutool工具扫描path得到包下类的集合,然后从集合中过滤留下使用了Component注解的class。是不是还挺简单的?


当然,这里类还没有被实例化,所以这里只是打印一下类名看看效果:


1112728-20220404143201789-9248132.png


从结果可以看到类已经获取到了。下一步实例化。

 

手撕Spring实例化对象流程

 

实例化对象,主要有两种作用域。一种是单例的,一种是多例的。这又要给用户选择了。给用户选择的地方都是通过注解。再将Component注解类复制一份,改名叫Scope。value默认是singleon,单例。


1112728-20220404143219761-1084426292.png


实例化对象需要一个容器来存放,首先要定义一个对象BeanDefination来存放对象的描述:


1112728-20220404143234933-276768705.png


每个class对应一个BeanDefination,存放到一个map容器中:


1112728-20220404143249200-1348917445.png


要获取实例的时候需要判断是否是单例的,单例的就用一个map存起来下次获取时直接用,不是单例就直接实例化。


1112728-20220404143303650-611994043.png


运行看看效果:


1112728-20220404143321397-1593979525.png


这次不是null,而是具体的实例了。但是咱们不满足于用getBean获取呀,下面咱们来看怎么自动注入。

 

手撕Spring依赖注入流程

 

首先来定义一个标识注入的注解Autowired:


1112728-20220404143354297-1763732909.png


再任意定义一个Bean做注入测试:


1112728-20220404143419139-1234795464.png


注入到UserService对象中:


1112728-20220404143432017-520010534.png


把newInstance实例化的地方单独提取一个方法,实例化时获取类是否有Autowired注解的,有的化先实例化它:


1112728-20220404143444714-2050120821.png


测试效果:


1112728-20220404143458957-958209840.png


总结

 


这里面的代码咱们在平时业务开发中常用,我也没多解释。大家有没有感觉整个流程就像做业务开发一样顺畅。其实写底层容器就是这样,原理了解了一点都不难。

 

大家说完这篇之后建议读读其他spring的文章,试试是不是有了深层的理解?

 

Spring Boot 使用的经典错误-找不到Bean了

 

Spring Kafka的异步BUG

 

Java&Spring过时的经典语录

相关文章
|
3天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
14 2
|
19天前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
9天前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
35 9
|
1月前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
108 5
|
1月前
|
XML Java 数据格式
Spring底层架构源码解析(二)
Spring底层架构源码解析(二)
|
1月前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
167 2
|
1月前
|
设计模式 JavaScript Java
Spring 事件监听机制源码
Spring 提供了事件发布订阅机制,广泛应用于项目中。本文介绍了如何通过自定义事件类、订阅类和发布类实现这一机制,并展示了如何监听 SpringBoot 启动过程中的多个事件(如 `ApplicationStartingEvent`、`ApplicationEnvironmentPreparedEvent` 等)。通过掌握这些事件,可以更好地理解 SpringBoot 的启动流程。示例代码展示了从事件发布到接收的完整过程。
|
1月前
|
缓存 Java Spring
源码解读:Spring如何解决构造器注入的循环依赖?
本文详细探讨了Spring框架中的循环依赖问题,包括构造器注入和字段注入两种情况,并重点分析了构造器注入循环依赖的解决方案。文章通过具体示例展示了循环依赖的错误信息及常见场景,提出了三种解决方法:重构代码、使用字段依赖注入以及使用`@Lazy`注解。其中,`@Lazy`注解通过延迟初始化和动态代理机制有效解决了循环依赖问题。作者建议优先使用`@Lazy`注解,并提供了详细的源码解析和调试截图,帮助读者深入理解其实现机制。
29 1
|
1月前
|
XML Java 数据格式
手动开发-简单的Spring基于注解配置的程序--源码解析
手动开发-简单的Spring基于注解配置的程序--源码解析
46 0
|
1月前
|
XML Java 数据格式
手动开发-简单的Spring基于XML配置的程序--源码解析
手动开发-简单的Spring基于XML配置的程序--源码解析
79 0