手撕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 可以获取对象来使用。
那咱们就根据这两个特质自己写一个类,要点如下图1和2两步。
既然需要将configClass配置类作为参数传入,就先来构造一个空的配置类。
这样从表面上,就可以像使用AnnotationConfigApplicationContext一样使用Spring容器了。
为了标注重点和屏蔽用户终端差异带来的文章阅读体验差异,这里我直接用的截图。代码文字在 https://github.com/xiexiaojing/yuna 里可以找到。
手撕Spring扫描流程
所谓HelloWorld版本就是除了入门,其他没有任何作用。咱们希望它可以实现自动扫描文件夹下的带有@Component注解的Bean完成注入。首先自己来新建一个@Component注解:
并在UserService中使用这个注解:
当然,Component注解是用来扫描的,那还得来定义一个扫描注解,就是把Component注解复制一份,改个名字:
ComponentScan这个注解要加在YunaConfig上,通过它来加载扫描配置:
咱们再回到YunaApplicationContext,配置类定义完之后,咱们下一步上下文就来解析它:
这时候咱们回到main方法,运行结果:
咱们拿到了解析路径,但是UserService这个Bean还是空的。重点来了,下面的代码要注意看:
上面首先使用hutool工具扫描path得到包下类的集合,然后从集合中过滤留下使用了Component注解的class。是不是还挺简单的?
当然,这里类还没有被实例化,所以这里只是打印一下类名看看效果:
从结果可以看到类已经获取到了。下一步实例化。
手撕Spring实例化对象流程
实例化对象,主要有两种作用域。一种是单例的,一种是多例的。这又要给用户选择了。给用户选择的地方都是通过注解。再将Component注解类复制一份,改名叫Scope。value默认是singleon,单例。
实例化对象需要一个容器来存放,首先要定义一个对象BeanDefination来存放对象的描述:
每个class对应一个BeanDefination,存放到一个map容器中:
要获取实例的时候需要判断是否是单例的,单例的就用一个map存起来下次获取时直接用,不是单例就直接实例化。
运行看看效果:
这次不是null,而是具体的实例了。但是咱们不满足于用getBean获取呀,下面咱们来看怎么自动注入。
手撕Spring依赖注入流程
首先来定义一个标识注入的注解Autowired:
再任意定义一个Bean做注入测试:
注入到UserService对象中:
把newInstance实例化的地方单独提取一个方法,实例化时获取类是否有Autowired注解的,有的化先实例化它:
测试效果:
总结
这里面的代码咱们在平时业务开发中常用,我也没多解释。大家有没有感觉整个流程就像做业务开发一样顺畅。其实写底层容器就是这样,原理了解了一点都不难。
大家说完这篇之后建议读读其他spring的文章,试试是不是有了深层的理解?
编程一生
因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。
PDCA方法论,检查自己是否错过更新:每周三晚上8点左右,我都会更新文章,如果你没有收到,记得点开【编程一生】公众号找一下(*^▽^*)。如果没有找到,那可能是像本周一样,我记错了日子(꒦_꒦)
手撕spring核心源码,彻底搞懂spring流程的更多相关文章
- 3 手写Java HashMap核心源码
手写Java HashMap核心源码 上一章手写LinkedList核心源码,本章我们来手写Java HashMap的核心源码. 我们来先了解一下HashMap的原理.HashMap 字面意思 has ...
- 1 手写Java ArrayList核心源码
手写ArrayList核心源码 ArrayList是Java中常用的数据结构,不光有ArrayList,还有LinkedList,HashMap,LinkedHashMap,HashSet,Queue ...
- 6 手写Java LinkedHashMap 核心源码
概述 LinkedHashMap是Java中常用的数据结构之一,安卓中的LruCache缓存,底层使用的就是LinkedHashMap,LRU(Least Recently Used)算法,即最近最少 ...
- 2 手写Java LinkedList核心源码
上一章我们手写了ArrayList的核心源码,ArrayList底层是用了一个数组来保存数据,数组保存数据的优点就是查找效率高,但是删除效率特别低,最坏的情况下需要移动所有的元素.在查找需求比较重要的 ...
- Android版数据结构与算法(五):LinkedHashMap核心源码彻底分析
版权声明:本文出自汪磊的博客,未经作者允许禁止转载. 上一篇基于哈希表实现HashMap核心源码彻底分析 分析了HashMap的源码,主要分析了扩容机制,如果感兴趣的可以去看看,扩容机制那几行最难懂的 ...
- Java内存管理-掌握类加载器的核心源码和设计模式(六)
勿在流沙筑高台,出来混迟早要还的. 做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开! 上一篇文章介绍了类加载器分类以及类加载器的双亲委派模型,让我们能够从整体上对类加载器有 ...
- 并发编程之 SynchronousQueue 核心源码分析
前言 SynchronousQueue 是一个普通用户不怎么常用的队列,通常在创建无界线程池(Executors.newCachedThreadPool())的时候使用,也就是那个非常危险的线程池 ^ ...
- iOS 开源库系列 Aspects核心源码分析---面向切面编程之疯狂的 Aspects
Aspects的源码学习,我学到的有几下几点 Objective-C Runtime 理解OC的消息分发机制 KVO中的指针交换技术 Block 在内存中的数据结构 const 的修饰区别 block ...
- Backbone事件机制核心源码(仅包含Events、Model模块)
一.应用场景 为了改善酷版139邮箱的代码结构,引入backbone的事件机制,按照MVC的分层思想搭建酷版云邮局的代码框架.力求在保持酷版轻量级的基础上提高代码的可维护性. 二.遗留问题 1.b ...
随机推荐
- php session 的使用方法
public function insert(Request $request){ $parm = $request->except('token'); $this->validate($ ...
- Java基础——StringBuilder
/* StringBuilder作用: String 在new一个新的对象以及进行字符串拼接时,会在常量池创建拼接之前的两个字符串对象,拼接完成后会造成内存浪费,时间浪费 StringBuilder ...
- 阿里云镜像站DNS——Chrome配置方法
镜像下载.域名解析.时间同步请点击 阿里巴巴开源镜像站 DNS 简介 域名系统(服务)协议(DNS)是一种分布式网络目录服务,主要用于域名与 IP 地址的相互转换,以及控制因特网的电子邮件的发送. 阿 ...
- 使用 Docker 部署 LNMP 并搭建 wordpress
准备 系统版本:CentOS Linux release 7.4.1708 (Core) 内核版本:3.10.0-693.el7.x86_64 IP:192.168.31.43 可访问 ...
- bitsadmin windwos自带下载命令
bitsadmin的四种下载文件的方法 一. bitsadmin /rawreturn /transfer getfile http://qianxiao996.cn/1.txt c:\1.txt 二 ...
- SMB共享配置
SMB 使用命令挂载和卸载SMB文件系统 自动挂载SMB文件系统 红帽企业 ...
- Java 框架、库和软件的精选列表(awesome java)
原创翻译,原始链接 本文为awesome系列中的awesome java Awesome Java Java 框架.库和软件的精选列表 项目 Bean映射 简化 bean 映射的框架 dOOv - 为 ...
- 从0到1搭建k8s集群系列1:安装虚拟机及docker
前言 本系列文章记录了本人学习k8s集群搭建的过程,从k8s基本组件的安装.到部署mysql服务到k8s集群.部署web项目到k8s集群以及安装可视化界面管理工具kuboard. 因为k8s的组件安装 ...
- JDBC 能否处理 Blob 和 Clob?
Blob 是指二进制大对象(Binary Large Object),而 Clob 是指大字符对象 (Character Large Objec),因此其中 Blob 是为存储大的二进制数据而设计的, ...
- js的json序列化和反序列化
(1)序列化 即js中的Object转化为字符串 1.使用toJSONString var last=obj.toJSONString(); //将JSON对象转化为JSON字符 2.使用string ...