【学习底层原理系列】重读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

好,第一篇就先建立基本的印象。

原文地址:https://www.cnblogs.com/xyang/p/12346784.html

时间: 2024-10-11 09:41:46

【学习底层原理系列】重读spring源码1-建立基本的认知模型的相关文章

Spring源码阅读系列总结

最近一段时间,粗略的查看了一下Spring源码,对Spring的两大核心和Spring的组件有了更深入的了解.同时在学习Spring源码时,得了解一些设计模式,不然阅读源码还是有一定难度的,所以一些重要的设计模式简单的做了阐述.同时还会简单的加入一些GOF中提到的设计原则.Spring的源码阅读系列,也暂告一段落.下面是就带你走进Spring世界: Spring系列的引子 1)Spring WebApplicationContext初始化与消亡 这一节帮我们了解Spring是如何初始化WebAp

死磕Spring系列之一:准备阅读Spring源码环境

死磕Spring系列前言 死磕spring系列博客,是对Spring进行源码级阅读.工作以来,一直接触spring框架,可以说对spring框架的配置使用已经非常熟练了.个人感觉:Spring技术非常强大,简单的xml标签配置,就可以开启非常强大的支持功能,囊括J2EE企业应用的方方面面.使用归使用,但是却对spring底层设计和实现,一知半解."到底是什么优秀的设计,能让Spring无所不能,无所不包".最后,就有了我想研读Spring 源码的动力. 阅读任何任何一门框架源码,其实和

Spring源码学习笔记(6)

Spring源码学习笔记(六) 前言-- 最近花了些时间看了<Spring源码深度解析>这本书,算是入门了Spring的源码吧.打算写下系列文章,回忆一下书的内容,总结代码的运行流程.推荐那些和我一样没接触过SSH框架源码又想学习的,阅读郝佳编著的<Spring源码深度解析>这本书,会是个很好的入门. 上一篇中我们梳理到 Spring 加载 XML 配置文件, 完成 XML 的解析工作,接下来我们将进入 Spring 加载 bean 的逻辑. 我们使用 Spring 获取 XML

Spring源码学习笔记(3)

Spring源码学习笔记(三) 前言----     最近花了些时间看了<Spring源码深度解析>这本书,算是入门了Spring的源码吧.打算写下系列文章,回忆一下书的内容,总结代码的运行流程.推荐那些和我一样没接触过SSH框架源码又想学习的,阅读郝佳编著的<Spring源码深度解析>这本书,会是个很好的入门. DispatcherServlet 实现核心功能 和普通的 Servelt 类一样, DispatcherServlet 中的 doGet() 和 doPost() 方法

spring源码剖析(六)AOP实现原理剖析

Spring的AOP实现原理,酝酿了一些日子,写博客之前信心不是很足,所以重新阅读了一边AOP的实现核心代码,而且又从网上找了一些Spring Aop剖析的例子,但是发现挂羊头买狗肉的太多,标题高大上,内容却大部分都是比较浅显的一些介绍,可能也是由于比较少人阅读这部分的核心代码逻辑把,然后写这部分介绍的人估计也是少之又少,不过说实话,Spring Aop的核心原理实现介绍确实不太好写,里面涉及的类之间的调用还是蛮多的,关系图画的太细的画也很难画,而且最重要的一点就是,如果对AOP的概念以及spr

Spring源码学习笔记(5)

Spring源码学习笔记(五) 前言-- 最近花了些时间看了<Spring源码深度解析>这本书,算是入门了Spring的源码吧.打算写下系列文章,回忆一下书的内容,总结代码的运行流程.推荐那些和我一样没接触过SSH框架源码又想学习的,阅读郝佳编著的<Spring源码深度解析>这本书,会是个很好的入门 写下一句话,开篇不尴尬  ----  上篇文章中梳理到 Spring 加载资源文件后开始解析 Bean, 现在我们从两个解析函数 parseDefaultElement() 和 par

Spring 源码学习(二)

容器概述 IoC也被称作依赖注入(DI).它是一个处理对象依赖项的过程,也就是将他们一起工作的其他的对象,只有通过构造参数.工厂方法参数或者(属性注入)通过构造参数实例化或通过工厂方法返回对象后再设置属性.当创建bean后,IoC容器再将这些依赖项注入进去.这个过程基本上是反转的,因此得名控制反转(IoC). 下图是 IoC 的高级别视图 IoC容器利用Java的POJO类和配置元数据来生成 完全配置和可执行 的系统或应用程序.而Bean在Spring中就是POJO,也可以认为Bean就是对象.

Spring 源码学习(一)

设计伊始       Spring 是为解决企业级应用开发的复杂性而设计,她可以做很多事.但归根到底支撑Spring的仅仅是少许的基本理念,而所有地这些的基本理念都能可以追溯到一个最根本的使命:简化开发.这是一个郑重的承诺,其实许多框架都声称在某些方面做了简化. 而Spring则立志于全方面的简化Java开发.对此,她主要采取了4个关键策略: 1,基于POJO的轻量级和最小侵入性编程: 2,通过依赖注入和面向接口松耦合: 3,基于切面和惯性进行声明式编程: 4,通过切面和模板减少样板式代码: 而

Spring源码学习笔记(7)

Spring源码学习笔记(七) 前言-- 最近花了些时间看了<Spring源码深度解析>这本书,算是入门了Spring的源码吧.打算写下系列文章,回忆一下书的内容,总结代码的运行流程.推荐那些和我一样没接触过SSH框架源码又想学习的,阅读郝佳编著的<Spring源码深度解析>这本书,会是个很好的入门 写前说句话, 开篇不尴尬 ---- 接下的这一篇当中, 我们将来回顾 Spring 中 AOP 功能的实现流程.  早上精力充沛, 开始新一天的学习 \(^o^)/~ 接触过 Spri