`
zk_chs
  • 浏览: 215447 次
  • 性别: 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的整个加载流程有个大概的轮廓,看起来容易理解, 毕竟文章这么长.....

相关推荐

    springBean加载过程源码解析文档,附有代码类名和行数

    Spring Bean 加载过程是 Spring 框架中最核心的部分之一,涉及到 ApplicationContext 的初始化、Bean 的加载和注册等过程。在 Spring Boot 应用程序中,SpringApplication 负责加载和管理 Bean。 SpringApplication...

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

    在本篇文章中,我们将深入探讨Spring源码中关于Bean初始化的过程,特别是`finishBeanFactoryInitialization()`方法和`preInstantiateSingletons()`方法。 首先,`finishBeanFactoryInitialization(Confi‌...

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

    本篇文章主要分析了Spring如何通过`ClassPathXmlApplicationContext`来启动和初始化Bean的过程。 首先,`ClassPathXmlApplicationContext`是Spring的一种ApplicationContext实现,用于从类路径下的XML配置文件加载...

    Spring的IoC容器初始化源码解析

    ### Spring的IoC容器初始化源码解析 #### 一、Spring框架的核心——IoC容器 Spring框架是一个开源的轻量级Java开发框架,其核心功能是IoC(Inversion of Control,控制反转)容器和AOP(Aspect Oriented ...

    spring-context源码

    在bean初始化时,`BeanFactory`会根据`BeanDefinition`中的依赖关系,自动将其他bean注入到当前bean的属性中。这种过程称为属性设置,可以通过setter方法、构造函数或者字段注入实现。源码中的`...

    Spring源码深度解析第二版

    Spring源码深度解析第二版 Spring是一款广泛应用于Java企业级应用程序的开源框架,旨在简化Java应用程序的开发和部署。Spring框架的核心主要包括了IoC容器、AOP、MVC框架等模块。 第1章 Spring整体架构和环境搭建 ...

    Spring实例化Bean顺序

    但是,这并不总是可靠的,因为Spring通常会延迟初始化Bean,除非显式要求或存在依赖关系。 2. **依赖注入**:Spring容器会根据Bean之间的依赖关系来决定实例化顺序。如果一个Bean依赖于另一个Bean,那么被依赖的...

    spring源码中英文注释

    7. **Bean的生命周期**:从bean的实例化、属性注入、初始化方法调用到销毁,Spring管理着bean的整个生命周期。`InitializingBean`和`DisposableBean`接口,以及`@PostConstruct`和`@PreDestroy`注解,是用来控制生命...

    spring3.2 源码 jar包

    最后,深入理解Spring源码不仅可以帮助你解决实际开发中的问题,还能让你更好地掌握Java企业级应用的开发技巧,为你的职业生涯增色添彩。因此,花时间研究这个源码jar包是非常值得的投资。祝你在学习Spring的道路...

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

    本文将深入探讨如何在Spring容器初始化Bean和销毁Bean前后执行自定义的操作,以便于进行日志记录、资源清理等任务。 首先,我们需要了解Spring中Bean的生命周期。Bean的生命周期大致分为以下阶段: 1. 实例化:...

    spring源码合集spring源码合集

    7. **Bean生命周期源码解析**:"05-Spring之Bean生命周期源码解析下-周瑜"将详细阐述Bean从创建到销毁的整个过程,包括初始化、后置处理、正常运行和销毁等阶段,使我们能更好地控制和管理Bean的状态。 8. **模拟...

    【框架源码篇 04】Spring源码手写篇-Bean定义配置化

    属性值可以用于初始化Bean的成员变量,或者作为构造函数参数。 Spring源码手写篇旨在帮助开发者理解Spring内部的工作原理,通过模仿Spring的核心功能,如Bean工厂和Bean定义,我们可以更好地掌握Spring框架的精髓。...

    spring-beans源码

    在解析BeanDefinition时,Spring会根据属性值和构造参数解析依赖,并在实例化Bean后进行注入。 5. **Bean的生命周期管理** Spring提供了对Bean生命周期的完整控制,包括初始化、正常运行以及销毁阶段。Bean可以...

    Spring2(源码)

    Spring框架是Java开发中最常用的轻量级开源框架之一,它...以上是对Spring框架的一些核心概念和关键点的介绍,深入研究Spring源码可以帮助我们更好地理解其实现细节,提高开发效率,并为定制化开发和性能优化提供基础。

    一步步深入理解Spring内部原理-带源码

    容器负责初始化、配置、装配以及管理Bean。Bean的定义通常包含在XML、Java注解或Java配置类中。 4. **面向切面编程(Aspect-Oriented Programming, AOP)**:AOP允许开发者定义“切面”,即关注点的模块化,如日志...

    Spring源码学习四:BeanDefinition装载前奏曲1

    在深入Spring源码的学习过程中,我们主要关注BeanDefinition的装载过程。BeanDefinition是Spring框架的核心概念,它包含了关于Bean的所有元数据,如类名、属性、依赖关系等。在Spring初始化时,会读取XML配置文件,...

    spring bean life cycle

    在Spring框架中,Bean生命周期是核心概念之一,它涉及到Bean的创建、初始化、使用和销毁等阶段。了解和掌握Bean生命周期对于开发高质量的Spring应用至关重要。以下是对Spring Bean生命周期的详细解析。 首先,Bean...

    Spring Bean重复执行两次(实例被构造两次)问题分析

    这个问题可能是由多种原因引起的,涉及到Spring的初始化过程和容器的行为。以下是对该问题的详细分析: 首先,我们需要理解Spring Bean的生命周期。在Spring容器初始化时,它会根据配置加载Bean的定义,并根据需要...

    Spring IoC源码深度剖析开源架构源码2021.pdf

    文档可能将深入探讨Spring IoC容器初始化、Bean生命周期管理、依赖注入等关键概念,并以2021年的开源版本为背景进行分析。 从提供的部分文档内容来看,我们可以提炼出以下几个知识点: 1. **BeanFactory与...

Global site tag (gtag.js) - Google Analytics