【学习底层原理系列】重读spring源码1-建立基本的认知模型
开篇闲扯
在工作中,相信很多人都有这种体会,与其修改别人代码,宁愿自己重写。
为什么?
先说为什么愿意自己写:
从0-1的过程,是建立在自己已有认知基础上,去用自己熟悉的方式构建一件作品。也就是说,
1.对目标的认知是熟悉的(当然每个人水平可能不一样,也有可能是错的,这不重要,重要的是自认为是符合的);
2.使用的工具是自己熟悉的。
接下来就是去做一件自己熟悉的领域的事而已。
再说为什么不愿意修改别人代码:
1.首先要在阅读代码过程中,去不断的检视对方的实现是否符合自己的认知,很可能由于双方认识程度不同,导致一开始对目标的认识就不一致。
2.实现方式是自己不熟悉的,要不断的去适应对方的风格,要不断的去猜测对方的用意,还要记忆大量的内容。
3.人都有趋利避害的心里,对于自己不熟悉的东西进行阅读,是有读错的风险的,如果再修改,那风险就更大了。对这种风险的规避,是来自骨子里的。
在猿界,阅读源码的经验是非常被看重的
你很多人工作了很多年,却总是不能沉下心来读一读
这里扯了这么多,算是一种让自己心安的解释吧
下面开始正文,尝试换一种风格来解析spring源码。因为看了网上很多人写的文章,就是粘了一堆代码,简单的做了解释,个人认为这并不具有可操作性,与其这样看只言片语的代码,还不如直接看源码来的完整。
Spring临门一脚
我们知道,spring的两大核心功能是IOC和AOP。其中IOC解决了我们用的实例的创建问题,AOP解决的是对方法的扩展问题。不管是出于什么考虑,初衷都是为了减少代码编写,减少在非业务开发之外的精力。
今天我们先来学习IOC:
依赖注入,或者叫控制反转。不熟悉的可以自行查一下,无非概念而已。
Java是面向对象的语言,在没有Spring之前,甚至于现在我们在开发过程中,需要用到某对象了,我们是这么来做的:
MyObject obj=new MyObject();
... obj.methodName(); ...
于是你会发现,到处都充斥着这种实例初始化的代码,可能在类变量里,也可能在方法的局部变量里。于是有人就想了,我能不能把这些变量统一管理起来呢?比如统一放在类变量里,可以在当前类实例化时在构造方法里统一实例化,也可以在声明时就实例化。比如这样:
public class MySuperClass1{
  MySubClassA subA=new MySubClassA();
  MySubClassB subB=new MySubClassB(); 
MySuperClass2 super2=new MySuperClass2();
...
public void super1Methord(){
super2.super2Methord(this.subA);
}
}
嗯,看起来好了很多,这样在MySuperClass1类中,无论有多少个方法用到了那两个sub类的实例,都不用再自己实例化了。
那么问题来了,如果需要在方法super1Methord()中调用另一个类MySuperClass2的方法super2Methord()【如代码所示】,而这个方法也用到了MySubClassA的实例,怎么办?聪明的你肯定想到了,把对象作为参数传递进去,就如代码中一样。
那么问题又来了,假如在第三个类MySuperClass3中,存在着和MySuperClass1一样的情况,那么该怎么办呢?是不是还要自己创建对象,然后传递进去?这样不就是重复创建吗?那怎么办才能更好一些呢?
可能你会想到,我弄一个根类,所有类都继承自这个类,在这个根类里实例化好所有对象,然后就不用重复创建了。
是的,思路是对的,只是,这就需要自己来维护这些类,如果新增了,就要时刻记得去根类中添加一下,如果不需要了,要记得去根类中删除下,项目小还好,项目大了,谁还记得哪个有用哪个没用?最后这个根类,就谁都不敢轻易改。那有没有什么好的方式可以解决呢?比如我配置下,或者加个注解,这个根类就能自动识别我新加的类,就能给自动的实例化?
springIOC,就做了这件事。它提供了容器,也就是我们说的根类,我们在使用的时候,就可以通过名字或其他方式,从容器中拿到事先创建好的实例对象。
ApplicationContext ctx = new ClassPathXmlApplicationContext("aop-test.xml");
ITestBean testBean = (ITestBean) ((ClassPathXmlApplicationContext) ctx).getBean("testBean");
String str = testBean.getStr();
对应到源码里,容器就是各种xxxApplicationContext,例如上面代码中的ClassPathXmlApplicationContext。
我觉得以上是一定要理解清晰的知识点。知道了what,再带着疑问和目标去了解How,会事半功倍。
下面开始分析源码,在分析源码过程中,我会从繁杂的代码中把主流程梳理出来,尝试去掉细枝末节,尽量保证思路的连贯性。
ApplicationContext ctx = new ClassPathXmlApplicationContext("aop-test.xml");
这就是初始化容器,我们跟进去,发现在其构造函数中,有一个核心方法是需要我们关注的:refresh()
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException { super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
为什么这里叫刷新呢?不是应该叫创建、构造之类的吗?
在接口注释里有一句话:
As this is a startup method, it should destroy already created singletons
作为一个启动方法,它需要销毁已创建的单例。
销毁后然后再创建。这不就是刷新的意思吗。
继续跟进去:
refresh()方法里,最核心的12个方法,共同支撑起Spring的架子。
public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // 1 刷新前的预处理:
            prepareRefresh();
            // 2 【创建bean】生成BeanFactory,并加载beanDefinition
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            // 3 对BeanFactory进行预处理:
            prepareBeanFactory(beanFactory);
            try {
                //4 空实现,暂时忽略
                postProcessBeanFactory(beanFactory);
                // 5 【扩展点】添加BeanFactory的后置处理器BeanFactoryPostProcessor并执行之。用于在bean实例化之前,读取beanDefinition并进行修改。
                invokeBeanFactoryPostProcessors(beanFactory);
                // 6 【扩展点】:添加BeanPostProcessor,注意区别于上述的BeanFactoryPostProcessor,这里只是添加,在第11步中,bean实例化时执行初始化方法之前后,可对bean进行修改
                registerBeanPostProcessors(beanFactory);
                // 7 初始化MessageSource组件,做国际化功能:消息绑定,消息解析
                initMessageSource();
                // 8 【事件监听】初始化事件处理器
                initApplicationEventMulticaster();
                // 9 在这里是空实现
                onRefresh();
                // 10 【事件监听】注册监听器,就是实现了ApplicationListener接口的bean,和上面的8搭配使用
                registerListeners();
                // 11 【创建bean】初始化非懒加载的单例bean,两个作用
          // .真正的实例化bean
          // .实现Aop,创建代理bean
                finishBeanFactoryInitialization(beanFactory);
                // 12 【事件监听】完成context的刷新,发布ContextRefereshedEvent事件
                finishRefresh();
            }
            catch (BeansException ex)// 忽略
            }
            finally {
                // 忽略
            }
        }
    }
其实归纳下,需要重点关注的就分为以下三类:
1.【创建bean】:2和11
2.【扩展点】:处理器注册和执行,包括BeanFactoryPostProcessor和BeanPostProcessor:4,5,6,11
3.【事件监听】:8,10,12
好,第一篇就先建立基本的印象。
【学习底层原理系列】重读spring源码1-建立基本的认知模型的更多相关文章
- 线程池底层原理详解与源码分析(补充部分---ScheduledThreadPoolExecutor类分析)
		
[1]前言 本篇幅是对 线程池底层原理详解与源码分析 的补充,默认你已经看完了上一篇对ThreadPoolExecutor类有了足够的了解. [2]ScheduledThreadPoolExecut ...
 - 【学习底层原理系列】重读spring源码3-加载beanDefinition的方法obtainFreshBeanFactory
		
obtainFreshBeanFactory()方法概述 定义BeanFactory,并加载以下两种bean的定义,装配到BeanFactory: 1.配置文件中定义的bean 2.通过<con ...
 - HashMap底层原理及jdk1.8源码解读
		
一.前言 写在前面:小编码字收集资料花了一天的时间整理出来,对你有帮助一键三连走一波哈,谢谢啦!! HashMap在我们日常开发中可谓经常遇到,HashMap 源码和底层原理在现在面试中是必问的.所以 ...
 - 转 Spring源码剖析——核心IOC容器原理
		
Spring源码剖析——核心IOC容器原理 2016年08月05日 15:06:16 阅读数:8312 标签: spring源码ioc编程bean 更多 个人分类: Java https://blog ...
 - Spring源码学习
		
Spring源码学习--ClassPathXmlApplicationContext(一) spring源码学习--FileSystemXmlApplicationContext(二) spring源 ...
 - Netty 学习(十):ChannelPipeline源码说明
		
Netty 学习(十):ChannelPipeline源码说明 作者: Grey 原文地址: 博客园:Netty 学习(十):ChannelPipeline源码说明 CSDN:Netty 学习(十): ...
 - Spring源码系列 — 注解原理
		
前言 前文中主要介绍了Spring中处理BeanDefinition的扩展点,其中着重介绍BeanDefinitionParser方式的扩展.本篇文章承接该内容,详解Spring中如何利用BeanDe ...
 - 框架源码系列六:Spring源码学习之Spring IOC源码学习
		
Spring 源码学习过程: 一.搞明白IOC能做什么,是怎么做的 1. 搞明白IOC能做什么? IOC是用为用户创建.管理实例对象的.用户需要实例对象时只需要向IOC容器获取就行了,不用自己去创建 ...
 - Spring 源码学习系列
		
前言 Spring框架之于 JavaEE 程序员来说,犹如锄头之于农民.Java 程序员每天都要使用Spring框架,Spring框架也确实是个可手的工具. 最初使用Spring的时候,我们需要配置m ...
 
随机推荐
- 《【面试突击】— Redis篇》--Redis都有哪些数据类型?分别在哪些场景下使用比较合适?
			
能坚持别人不能坚持的,才能拥有别人不能拥有的.关注编程大道公众号,让我们一同坚持心中所想,一起成长!! <[面试突击]— Redis篇>--Redis都有哪些数据类型?分别在哪些场景下使用 ...
 - 如何使用F4的IRAM2内存
			
在使用KEIL做F4的项目的时候发现RAM区有片上IRAM2选项,查了datesheet后发现这块是CCM内存区 CCM内存是在地址0x1000000映射的64KB块,只提供CPU通过数据D总线进行访 ...
 - Mysql的binlog日志与mysqlbinlog命令
			
binlog相关 MySQL 的二进制日志 binlog 可以说是 MySQL 最重要的日志,它记录了所有的 DDL 和 DML 语句(除了数据查询语句select.show等),以事件形式记录,还包 ...
 - RegExp正则表达式基础
			
什么是正则表达式 正则表达式用于对字符串模式匹配及检索替换 如何创建正则 有两种创建正则的方法: js风格 new RegExp(pattern [, flags]) perl风格 /pattern/ ...
 - 11.黑窗口、IDEA生成JavaDoc
			
JavaDoc: 它是一种技术,可以将一些注释信息生成一个帮助文档,就类似于Java的API JavaAPI帮助文档: https://www.oracle.com/cn/java/technolog ...
 - spring boot 的中文乱码
			
首先 自检IDEA的编码 配置文件加入设置http tomcat spring.http.encoding.force=true spring.http.encoding.charset=UTF-8 ...
 - Mac 查看端口占用情况及杀死进程
			
查看端口占用情况命令 sudo lsof -i :9000 冒号后面就是你需要查看的端口号. TheEternityZhang-MacBook:project zhtony$ sudo lsof -i ...
 - Exchange Server 2016 本地部署安装流程
			
思路:一台Server 2016用作AD+DNS,一台Server 2016用作Exchange Server 2016 Exchange Server 2016 CU14 安装路径:安装路径:htt ...
 - Dynamics 365 CRM 在 Connected Field Service 中部署 IoT Central (二)- 匹配设备
			
上个blog中介绍了我们怎么去部署IoT central和 connected field service做连接. 我们这次介绍怎么把IoT设备在CRM中怎么去注册. 首先我们打开devices,再选 ...
 - 史上最详细的二叉树、B树,看不懂怨我
			
今天我们要说的红黑树就是就是一棵非严格均衡的二叉树,均衡二叉树又是在二叉搜索树的基础上增加了自动维持平衡的性质,插入.搜索.删除的效率都比较高.红黑树也是实现 TreeMap 存储结构的基石. 1.二 ...