从最基础的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. Socket引子

    === ''' Socket网络编程: --应用层:http smtp dns ftp ssh snmp dhcp... 无论协议是什么本质上都是数据交换,总结为两种方式:收和发 --传输层(端口Po ...

  2. raise ValueError("Cannot convert {0!r} to Excel".format(value))

    I have hundreds of XML files that I need to extract two values from and ouput in an Excel or CSV fil ...

  3. request.getParameter("name")乱码问题

    1 页面提交http的get请求时,找到页面的字符编码<%@ page contentType="text/html;charset=utf-8" language=&quo ...

  4. springBoot 搭建web项目(前后端分离,附项目源代码地址)

    springBoot 搭建web项目(前后端分离,附项目源代码地址)   概述 该项目包含springBoot-example-ui 和 springBoot-example,分别为前端与后端,前后端 ...

  5. 通俗易懂的php多线程解决方案

    我们在做项目的时候,有些需求,特别是数据的响应处理需要花费大量的时间,由于php是一个短生命周期的脚本语言,到了默认的30秒,php的数据处理还没完成,php的生命周期就结束了.这时需要使用异步并发处 ...

  6. 微信硬件平台(一) 公众号 ESP8266 Arduino LED

    微信硬件平台 本文目的,使用微信公众号控制ESP8266的LED开和关.进一步使用微信当遥控器(避免写APP或者IOS或者小程序),控制一切设备.给两个关键的总教程参考. 官网教程  微信硬件平台 微 ...

  7. .NET中使用Redis 转发

    .NET中使用Redis   Redis是一个用的比较广泛的Key/Value的内存数据库,新浪微博.Github.StackOverflow 等大型应用中都用其作为缓存,Redis的官网为http: ...

  8. ind2sub

    ind2sub 线性索引的下标 语法 [I,J] = ind2sub(siz,IND)[I1,I2,I3,...,In] = ind2sub(siz,IND)   说明 ind2sub 函数确定与数组 ...

  9. AI tensorflow MNIST

    MNIST 数据 train-images-idx3-ubyte.gz:训练集图片 train-labels-idx1-ubyte.gz:训练集图片类别 t10k-images-idx3-ubyte. ...

  10. bootstrap 失效的原因

    首先必须对着官方文档的模板来写: https://getbootstrap.com/docs/4.3/getting-started/introduction/ 然后: 这里必须加上 rel=&quo ...