从最基础的Hello World开始。

spring的Hello World就三行代码:

public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
SomeBean someBean= (SomeBean) context.getBean("someBean");
someBean.doSomething();
}

这个hello world非常简单,通过xml文件,创建一个容器context,然后从容器中获取一个bean。

运行完这段代码后,问自己两个问题:

  • 容器创建时做了什么?
  • getBean()时又做了什么?

虽然这个例子是用的xml配置,但是搞懂这两个问题,对于另外两种配置方式,注解配置和Java Config,也就顺理成章了,原理都一样的。

如何才能知道?调试

好,开始回答第一个问题,容器创建时做了什么?

不管是xml,还是注解、Java Config,这些都是为了方便使用者而设计的,JVM可不知道说<bean>这个标签是啥意思,所以自然的,Spring需要对这些配置进行解析。

比如对于xml,就用XmlBeanDefinitionReader,把你写的beans.xml解析出来,你要创建什么对象,这些对象依赖哪些对象,是不是要懒加载(lazy-init),是不是单例...... 通通解析出来,放到一个叫BeanDefinition的对象里头,有多少种对象,就有多少个BeanDefinition,然后把这些BeanDefinition放到一个Map里头。

BeanDefinition有什么用?当然是为了后面实例化Bean用的,为什么要把配置信息放到BeanDefinition里?自然是不想每次需要实例对象时都去解析配置信息。

创建完所有BeanDefinition之后,会马上实例化对象吗?

如果用的是BeanFactory作为容器,则不会,对象默认都是懒加载,也就是在你想获取的时候再创建;

如果用的是上面hello world里的ApplicationContext ,则会马上实例化所有非懒加载的bean。

怎么实例化呢?这时候BeanDefinition就派上用场了,利用BeanDefinition里面的类信息,再用上反射,很容易就可以new出一个实例;

那如果bean里面依赖其他bean呢?那就顺带把其他bean也实例化出来,然后通过构造函数或者set方法,注入到bean里面去。

实例化后的bean,就直接返回给你了吗?这可不行,单例对象可是要复用的,Spring容器会被new出来的对象,放到又一个Map里面,这也解释了为什么bean不会被GC回收,因为bean通过Map和容器关联了,而容器对象是GC Root。当然,上面讲的仅限于单例,多例可不会放到Map里,容器创建完就直接丢出去了,让对象自生自灭,该回收时就回收。

第一个问题回答结束。

理解了第一个问题,第二个问题就很简单了,获取bean时又做了什么?

很简单:

  • 如果bean是单例,并且还没实例化,那就按照上面的流程new一个,如果已经实例化了,就直接返回;
  • 如果是多例,new一个返回

第二个问题回答结束。

稍微总结一下。怎样读源码?先学会怎么用,再去弄懂为什么。知其然知其所以然,首先要知其然啊。

读源码跟读书很像的,带着疑问去阅读,效率会高很多

  先粗读,也就是不断的单步调试,不必每个方法都step into想一看究竟,多step over,了解一下大概,然后记下疑问,进行第二版精读。

  精读就要弄懂每一行代码吗?不必,只看你关心的,Spring IoC的实际逻辑比我上面讲的要复杂的多,包括一些如果放在bean在实例化的过程中,拒绝掉新的实例化请求等线程安全问题,这些都不是你关心的重点,看到了快速跳过即可,只看你关心的。

对于spring的源码是否要看,观点是:

  建议不要硬着头皮看spring代码,本身的代码800多m,就是不上班开始看也不知道什么时候看完。如果想学学ioc,控制反转这些建议看看jodd项目,比较简练,但是我仍然不建议过多的看这些框架的代码,因为这些代码要完成任务需要很多琐碎的类实现,比如读取某个包下面的所有类,解析class的头文件,反射各种信息,再加上封装,很有可能在读源码的过程中掉到各种细节里出不来,所以读这种源码要事无巨细,理解原理即可。
  基本原理其实就是通过反射解析类及其类的各种信息,包括构造器、方法及其参数,属性。然后将其封装成bean定义信息类、constructor信息类、method信息类、property信息类,最终放在一个map里,也就是所谓的container,池等等,其实就是个map。。汗。。。。当你写好配置文件,启动项目后,框架会先按照你的配置文件找到那个要scan的包,然后解析包里面的所有类,找到所有含有@bean,@service等注解的类,利用反射解析它们,包括解析构造器,方法,属性等等,然后封装成各种信息类放到一个map里。每当你需要一个bean的时候,框架就会从container找是不是有这个类的定义啊?如果找到则通过构造器new出来(这就是控制反转,不用你new,框架帮你new),再在这个类找是不是有要注入的属性或者方法,比如标有@autowired的属性,如果有则还是到container找对应的解析类,new出对象,并通过之前解析出来的信息类找到setter方法,然后用该方法注入对象(这就是依赖注入)。如果其中有一个类container里没找到,则抛出异常,比如常见的spring无法找到该类定义,无法wire的异常。还有就是嵌套bean则用了一下递归,container会放到servletcontext里面,每次reQuest从servletcontext找这个container即可,不用多次解析类定义。如果bean的scope是singleton,则会重用这个bean不再重新创建,将这个bean放到一个map里,每次用都先从这个map里面找。如果scope是session,则该bean会放到session里面。仅此而已,没必要花更多精力。建议还是多看看底层的知识。

关于spring的源码的理解的更多相关文章

  1. 曹工说Spring Boot源码(21)-- 为了让大家理解Spring Aop利器ProxyFactory,我已经拼了

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  2. Spring IoC源码解析——Bean的创建和初始化

    Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和AOP的容器框架,主要是针对JavaBean的生命周期进行管理的轻量级容器 ...

  3. Spring:源码解读Spring IOC原理

    Spring IOC设计原理解析:本文乃学习整理参考而来 一. 什么是Ioc/DI? 二. Spring IOC体系结构 (1) BeanFactory (2) BeanDefinition 三. I ...

  4. spring事务源码解析

    前言 在spring jdbcTemplate 事务,各种诡异,包你醍醐灌顶!最后遗留了一个问题:spring是怎么样保证事务一致性的? 当然,spring事务内容挺多的,如果都要讲的话要花很长时间, ...

  5. 结合ThreadLocal来看spring事务源码,感受下清泉般的洗涤!

    在我的博客spring事务源码解析中,提到了一个很关键的点:将connection绑定到当前线程来保证这个线程中的数据库操作用的是同一个connection.但是没有细致的讲到如何绑定,以及为什么这么 ...

  6. Spring事务源码阅读笔记

    1. 背景 本文主要介绍Spring声明式事务的实现原理及源码.对一些工作中的案例与事务源码中的参数进行总结. 2. 基本概念 2.1 基本名词解释 名词 概念 PlatformTransaction ...

  7. SSH 之 Spring的源码(一)——Bean加载过程

    看看Spring的源码,看看巨人的底层实现,拓展思路,为了更好的理解原理,看看源码,深入浅出吧.本文基于Spring 4.0.8版本. 首先Web项目使用Spring是通过在web.xml里面配置 o ...

  8. Spring AOP 源码分析 - 拦截器链的执行过程

    1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...

  9. 框架源码系列六:Spring源码学习之Spring IOC源码学习

    Spring 源码学习过程: 一.搞明白IOC能做什么,是怎么做的  1. 搞明白IOC能做什么? IOC是用为用户创建.管理实例对象的.用户需要实例对象时只需要向IOC容器获取就行了,不用自己去创建 ...

随机推荐

  1. Maven中POM.XML详解

    转自https://blog.csdn.net/jariwsz/article/details/19554137 我们先看一个简单的例子: <project xmlns="http:/ ...

  2. python list和tuple

    list列表简介:列表是python的基础数据类型之⼀ ,其他编程语⾔也有类似的数据类型. 比如JS中的数组, java中的数组等等. 它是以[ ]括起来, 每个元素⽤' , '隔开⽽且可以存放各种数 ...

  3. C#基础知识之泛型集合转换为DataTable

    在做项目中,遇到了将集合转换为DataTable的使用,在网上看了资料,在这里记录下来,分享. using System; using System.Collections.Generic; usin ...

  4. Gulp的简单使用

    我比较喜欢使用Gulp,因为简单好用! 今天的任务是:使用Gulp来压缩 jQuery源码,各输出一个压缩的和未压缩的版本 第一步:安装 cnpm install gulp --save-dev cn ...

  5. Echarts的一些总结

    Echarts是专注做统计图表的插件,其本质是使用canvas进行图表的绘制.而如今它的属性和配置也是越来越丰富.基本的配置很简单,比如饼状图,就是数据和要显示的文字和颜色,柱状图,就是横纵坐标和数据 ...

  6. RPC入门总结(一)RPC定义和原理

    转载:深入浅出 RPC - 浅出篇 转载:RPC框架与Dubbo完整使用 转载:深入浅出 RPC - 深入篇 转载:远程调用服务(RPC)和消息队列(Message Queue)对比及其适用/不适用场 ...

  7. (转)Geoserver基础配图研究

    https://blog.csdn.net/zbcx_ZGIS/article/details/82216151 1面图层配图 1.1基本框架 这个是geoserver默认的polygon的代码,我们 ...

  8. [python] A*算法基于栅格地图的全局路径规划

    # 所有节点的g值并没有初始化为无穷大 # 当两个子节点的f值一样时,程序选择最先搜索到的一个作为父节点加入closed # 对相同数值的不同对待,导致不同版本的A*算法找到等长的不同路径 # 最后c ...

  9. 【Windows】+ windows下在某一文件夹下按“shift+鼠标右键”打开CMD窗口

    前言:没更改之前是打开的powershell窗口,超不习惯 新建一个txt文件,然后复制以下代码,然后保存为OpenCmdHere.reg 格式,然后双击即可 Windows Registry Edi ...

  10. QQ的ldw值计算方法