`
zk_chs
  • 浏览: 212483 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

spring加载过程,源码带你理解从初始化到bean注入

阅读更多

之前写过的很多spring文章,都是基于应用方面的,这次的话,就带大家来一次对spring的源码追踪,看一看spring到底是怎么进行的初始化,如何创建的bean,相信很多刚刚接触spring的朋友,或者没什么时间的朋友都很想知道spring到底是如何工作的。

 

首先,按照博主一贯的作风,当然是使用最新的spring版本,这次就使用spring4.2.5...其次,也是为了方便,采用spring-boot-1.3.3进行追踪,和spring 4.2.5是相同的。

不用担心框架不同,大家如果是使用的xml方式进行配置的话,可以去你的ContextListener里面进行追踪,spring-boot只是对 spring所有框架进行了一个集成,如果实在进行不了前面几个步骤的话,可以从文章第6步的AbstractApplicationContext开始看起, 这里就是spring最最重要的部分。

 

1、默认的spring启动器,DemoApplication:

该方法是spring-boot的启动器,我们进入。

 

2、进入了SpringApplication.java:

 

这里创建了一个SpringApplication,执行run方法,返回的是一个ConfigurableApplicationContext,这只是一个接口而已,根据名称来看,称作可配置的应用程序上下文。

 

3、我们不看new SpringApplication(Sources)过程了,有兴趣可以自己研究一下,里面主要是判断了当前的运行环境是否为web,当然,博主这次的环境是web,然后看run:

try语句块内的内容最为重要,因为创建了我们的context对象,此时需要进入的方法为

context = createAndRefreshContext(listeners, applicationArguments)

 

4、 接着往下看,看到context = createApplicationContext这行,进入,因为我们刚刚在创建SpringApplication时并没有给 this.applicationContextClass赋值,所以此时this.applicationContextClass = null,那么便会创建指定的两个applicationContext中的一个,返回一个刚刚创建的context,这个context便是我们的基 础,因为门现在为web环境,所以创建的context为 AnnotationConfigEmbeddedWebApplicationContext。

 

5、第4步创建了一个context,需要指出的是,context里面默认带有一个beanFactory,而这个beanFactory的类型为DefaultListableBeanFactory。

然后继续看我们的createAndRefreshContext方法,忽略别的代码,最重要的地方为refresh(context):

 

6、进入refresh(context),不管你进入那个实现类,最终进入的都是AbstractApplicationContext.java:

该方法中,我们这次需要注意的地方有两个:

1、invokeBeanFactoryPostProcessors(beanFactory);

2、finishBeanFactoryInitialization(beanFactory);

两处传入的beanFactory为上面的context中的DefaultListableBeanFactory。

 

7、进入invokeBeanFactoryPostProcessors(beanFactory):

然后找到第98行的invokeBeanDefinitionRegistryPostProcessors(priorityOrderedPostProcessors, registry),该方法看名字就是注册bean,进入。

 

8、 该方法内部有一个for循环,进入内部方法 postProcessor.postProcesBeanDefinitionRegistry(registry),此时传入的registry就是我们context中的beanfactory,因为其实现了BeanDefinitionRegistry接口。而此时的postProcessor实现类为ConfigurationClassPostProcessor.java。

 

 

9、进入之后直接看最后面的一个方法,名称为processConfigBeanDefinitions(registry),翻译过来就是配置beanDefinitions的流程。

 

10、在processConfigBeanDefinitions(registry)里,314行创建了一个parser解析器,用来解析bean。并在第321行进行了调用,那么我们进入parse方法。

 

11、进入parse方法之后,会发现内层还有parse方法,不要紧,继续进入内层的parse,然后会发现它们均调用了processConfigurationClass(ConfigurationClass configClass)方法:

12、 在processConfigurationClass(ConfigurationClass configClass)方法内,找到do循环,然后进入doProcessConfigurationClass方法,此时,便会出现许多我们常用的注 解了,spring会找到这些注解,并对它们进行解析。例如第268行的componentScanParser.parse方法,在这里会扫描我们的注 解类,并将带有@bean注解的类进行registry。

 

13、进入 componentScanParser.parse,直接进入结尾的scannner.doScan,然后便会扫描basepackages,并将扫描 到的bean生成一个一个BeanDefinitionHolder,BeanDefinitionHolder中包含有我们bean的一些相关信息、以 及spring赋予其的额外信息,例如别名:

 

14、 虽然已经创建了BeanDefinitionHolder,但并没有添加到我们的beanFactory中,所以需要执行263行的 registerBeanDefinition(definitionHolder, this.registry),进入后继续跳转:

然后看registry.registerBeanDefinition方法,因为我们的beanFactory为DefaultListableBeanFactory,所以进入对应的实现类。

 

15、在进入的registry.registerBeanDefinition方法中,关键点在851行或871行:

this.beanDefinitionMap.put(beanName, beanDefinition);

这个方法将扫描到的bean存放到了一个beanName为key、beanDefinition为value的map中,以便执行DI(dependency inject)。

 

16、现在我们回到第6步的第二条分支,此处是非懒加载的bean初始化位置,注意,我们之前只是对bean的信息进行了获取,然后创建的对象为BeanDefinition,却不是bean的实例,而现在则是创建bean的实例。

进入方法后找到829行的getBean(weaverAwareName):

 

17、getBean => getBeanFactory.getBean => doGetBean,然后找到306行的createBean,这里不讲语法,不要奇怪为什么这个createBean不能进入实现代码。

 

18、这之后的代码都比较容易追踪,直接给一条调用链:

doCreateBean(482) => createBeanInstance(510) => autowireConstructor(1034,1046) => autowireConstructor(1143) => instantiate(267) => instantiateClass(122) => newInstance(147)

括号内的数字代表行号,方便大家进行追踪,最后看到是反射newInstance取得的对象实例:

 

平时总说spring反射获取bean,其实也就是听别人这么说而已,还是自己见到才踏实,万一别人问你是不是通过Class.forName获取的呢?

 

19、属性注入,位于第18条的doCreateBean方法内,找到第543行,populateBean便译为填充Bean,进入后便能看到和我们平时代码对应的条件了,例如byType注入、byName注入:

这里还没有进行依赖注入,仅仅是准备一些必要的信息,找到1214行的ibp.postProcessPropertyValues方法
 
20、这里有很多实现类可以选择,因为博主平时是使用@Autowired注解,所以这里选择AutowiredAnnotationBeanPostProcessor,如果你使用@Resource的话,就选择CommonBeanPostProcessor:

 
21、进入该方法后,首先获取一些元信息metadata,通过findAutowiringMetadata获取,然后调用metadata.inject进行注入:

 
22、继续进入inject方法后,继续找到88行的element.inject方法并进入,实现类选择AutowiredFieldElement,该类是一个内部类:

在这个方法中,最重要的内容在第567~570行内,我们可以看到,这里其实也就是jdk的反射特性。
至此,spring的 bean初始化->注入 便完成了。

这次的博客内容很长(其实是自己追踪代码时间太久),感谢大家耐心看完,能有所收获的话便最好不过了。另外,若是有什么补充的话欢迎进行回复。

  • 大小: 143.7 KB
  • 大小: 204.8 KB
  • 大小: 53.1 KB
  • 大小: 106.1 KB
  • 大小: 336.1 KB
4
2
分享到:
评论
1 楼 WeaponLin 2017-03-20  
建议作者写个总结,这样对bean的整个加载流程有个大概的轮廓,看起来容易理解, 毕竟文章这么长.....

相关推荐

    Spring源码学习六:bean初始化1

    Spring源码学习六:bean初始化1

    spring的初始化

    spring的初始化,可以让初学者了解soring的开发过程,让开发轻松进入开发大门。

    Spring 源码分析(Bean的初始化)

    Spring 源码分析(Bean的初始化) 前言 本篇文章是个人第一次看spring源码并总结,同时也参考了下面这篇博客。基本也是按照他的思路来理解的。这也算是第一版个人简易理解。也算是窥见spring的冰山一角,之后也会...

    深入解析Spring IoC:源码与实践指南

    这包括从AnnotationConfigApplicationContext的实例化开始,到DefaultListableBeanFactory工厂的建立,再到AnnotatedBeanDefinitionReader的初始化,这一系列过程共同构成了Spring容器的加载核心。同时,本文还探讨...

    深入解析Spring IoC源码:核心机制与实践应用

    本文深入探讨了Spring框架中IoC容器的源码机制,涵盖了容器的初始化、Bean工厂的实例化、Bean定义的读取及Spring Bean的生命周期管理。通过精细的分析,本文揭示了AnnotationConfigApplicationContext的实例化过程,...

    Spring源码解析4章150页+Spring3.2.4中文注释源码

    4、Spring源码下载 二阶段 1、什么是IOC/DI 2、SpringIOC体系结构 3、源码分析-IOC容器的初始化 4、源码分析-IOC容器的依赖注入 5、源码分析-IOC容器的高级特性 三阶段 Spring AOP的涉及原理及具体实践 SpringJDBC...

    Spring源码简易篇,通过手写模拟了解Spring的底层源码启动和扫描逻辑,依赖注入等等过程

    通过手写模拟,了解Spring的底层源码启动过程 通过手写模拟,了解扫描逻辑和依赖注入等底层源码工作流程 通过手写模拟,了解初始化机制工作流程 通过手写模拟,了解BeanDefinition、BeanPostProcessor的概念 ...

    spring源码底层解析(1.81G)

    spring源码底层分析(1.81G)〖课程介绍〗:Spring启动时读取应用程序提供的Bean配置信息,并在Spring容器中生成一份相应的Bean配置注册表,然后根据这张注册表实例化Bean,装配好Bean之间的依赖关系,为上层应用提供...

    spring容器初始化bean和销毁bean之前进行一些操作的方法

    NULL 博文链接:https://bijian1013.iteye.com/blog/2374256

    spring boot实战.pdf高清无水印

    1.2.2 使用Spring Initializr初始化Spring Boot项目 10 1.3 小结 18 第2章 开发第一个应用程序 19 2.1 运用Spring Boot 19 2.1.1 查看初始化的Spring Boot新项目 21 2.1.2 Spring Boot项目构建过程解析 ...

    spring2.5.6源码

    rar包内含有spring2.5.6源码,解压即可使用 源代码分析,是一件既痛苦又快乐的事情,看别人写的代码是通过的,但当你能够看明白的时候,相信快乐也会随之而来,为了减少痛苦,更快的带来快乐,在这里希望通过这篇...

    spring boot源码

    4. 容器初始化过程中追加各种功能,例如统计时间、输出日志等 监听器类型 1. 在应用运行但未进行任何处理时,将发送 ApplicationStartingEvent。 2. 当Environment被使用,且上下文创建之前,将发送 ...

    Spring.html

    --全局初始化参数--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> 4.在Servlet中使用WebApplicationContextUtils获取容器...

    spring源代码解析

    从加载过程我们可以看到,首先从Servlet事件中得到ServletContext,然后可以读到配置好的在web.xml的中的各个属性值,然后ContextLoder实例化WebApplicationContext并完成其载入和初始化作为根上下文。当这个根上...

    Spring-Reference_zh_CN(Spring中文参考手册)

    3.3.5. 延迟初始化bean 3.3.6. 自动装配(autowire)协作者 3.3.6.1. 设置Bean使自动装配失效 3.3.7. 依赖检查 3.3.8. 方法注入 3.3.8.1. Lookup方法注入 3.3.8.2. 自定义方法的替代方案 3.4. bean的作用域 3.4.1. ...

    Spring攻略(第二版 中文高清版).part1

    2.7 自定义Bean初始化和析构 72 2.7.1 问题 72 2.7.2 解决方案 72 2.7.3 工作原理 72 2.8 用Java Config简化XML配置 77 2.8.1 问题 77 2.8.2 解决方案 77 2.8.3 工作原理 77 2.9 使Bean感知容器 ...

    Spring中文帮助文档

    3.3.4. 延迟初始化bean 3.3.5. 自动装配(autowire)协作者 3.3.6. 依赖检查 3.3.7. 方法注入 3.4. Bean的作用域 3.4.1. Singleton作用域 3.4.2. Prototype作用域 3.4.3. Singleton beans和prototype-bean的...

    《鲁班学院》子路老师spring底层原理分析视频全集.rar

    整个视频课程将由浅入深,介绍spring5源码的构建、spring5IOC容器的初始化过程、bean的声明周期过程、spring BeanFactoryPostporcessor并且结合原理给出当前流行的应用框架如何利用spring的源码知识写出优雅的代码,...

    Spring API

    3.3.4. 延迟初始化bean 3.3.5. 自动装配(autowire)协作者 3.3.6. 依赖检查 3.3.7. 方法注入 3.4. Bean的作用域 3.4.1. Singleton作用域 3.4.2. Prototype作用域 3.4.3. Singleton beans和prototype-bean的...

    SpringBoot (4.71G)精华详解

    1.Spring核心讲解与内容详述.mp4;`-{0B:r2E;]$u+m 2.Spring源码环境搭建与执行流程解析.mp4/j1};W;...14.Spring单例Bean的初始化与缓存流程详解.mp4%U7X2[;{3z 15.Spring对象属性赋值与作用域源码解

Global site tag (gtag.js) - Google Analytics