最近抽空看了下spring源码,我们一起从web容器启动开始,一步一步分析spring是如何被拉起,如何加载加载其配置文件,如何使用加载到容器上下文中的对象

web.xml

web.xml中的spring容器配置

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

ContextLoaderListner如何拉起?

ContextLoaderListener是spring核心功能启动器,该监听器会监听Servlet容器启动事件,一旦监听到该事件就拉起spring整个容器的启动。

  • ContextLoaderListener实现了javax.servlet.ServletContextListener接口,该接口的contextInitialized方法用来对扩展的容器进行初始化,ContextLoaderListener在该方法中对spring上下文进行初始化。
  • ContextLoaderListener继承ContextLoader,ContextLoader.initWebApplicationContext方法创建容器Context,并且调度配置文件加载。

ContextLoader.initWebApplicationContext方法中Context创建代码如下:

 if (this.context == null) {
this.context = createWebApplicationContext(servletContext);
}
if (this.context instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
if (!cwac.isActive()) {
// The context has not yet been refreshed -> provide services such as
// setting the parent context, setting the application context id, etc
if (cwac.getParent() == null) {
// The context instance was injected without an explicit parent ->
// determine parent for root web application context, if any.
ApplicationContext parent = loadParentContext(servletContext);
cwac.setParent(parent);
}
configureAndRefreshWebApplicationContext(cwac, servletContext);
}
}
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

初始化容器时需要判断容器是否已经初始化过,如果没有则创建一个新的spring容器

容器创建流程如下:

ContextLoader会首先从ServletContext中获取配置的容器类(参数名:contextClass)

如果获取不到则使用ContextLoader同一classPath下的ContextLoader.properties中的配置。

将org.springframework.web.context.WebApplicationContext对应的值作为容器类并实例化容器作为spring的根容器。ContextLoader.properties配置如下:

Context创建

创建Context实例,刷新Context。

刷新Context:创建Bean工厂,调用ResourceReader加载Spring的bean配置,将每个bean配置构建成一个BeanDefinition对象存储到Bean工厂,bean配置加载完成后需要给Bean工厂设置一系列postProccessor,执行这些Proccessor,Context在postProccessor处理完成后对所有“非懒加载”的单例bean对象进行实例化。实例化后执行BeanPostProcessor的BeanPostProcessorsBeforeInitialization方法,而后对指定了init方法的实例调用init方法,或者对实现了InitializingBean接口的bean调用afterPropertiesSet方法,初始化方法后执行BeanPostProcessor的applyBeanPostProcessorsAfterInitialization方法。实例化结束后向所有监听器推送ContextRefreshedEvent事件。其中供bean实例使用的BeanPostProcessor是ApplicationListenerDetector,通过该Proccessor,bean可以自定义init方法调用前后执行的动作。init执行前可以通过实现InitializingBean接口,init执行后可以通过实现ApplicationListener接口。

Bean工厂继承关系:

对于bean的具体实例化、初始化、

默认的XmlWebApplicationContext

XmlWebApplicationContext实例化后被存放到ServletContext中,缓存使用的key为:org.springframework.web.context.WebApplicationContext.ROOT

在web服务中只要能获取到ServletContext就可以根据WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE获取spring根Context

XmlWebApplicationContext实例化成功后,ContextLoader会调用configureAndRefreshWebApplicationContext对容器上下文进行配置和初始化

其中就包括Spring基本属性设置、配置文件加载,解析,对象实例化等动作。Spring容器上下文继承关系如下:

XmlWebApplicationContext的配置文件加载,解析,对象实例化等动作都统一在AbstractApplicationContext.refresh方法中完成

refresh结束后,容器会发布一个ContextRefreshedEvent事件,供依赖容器启动结束的组件或者第三方定制扩展使用(例如Dubbo,通过监听该事件触发其配置加载后的实例包装动作)。

具体的刷新核心代码我们看一下:

// Prepare this context for refreshing.
prepareRefresh(); // Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory); try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory); // Initialize message source for this context.
initMessageSource(); // Initialize event multicaster for this context.
initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses.
onRefresh(); // Check for listener beans and register them.
registerListeners(); // Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event.
finishRefresh();
}

  

BeanFactory

容器刷新涉及最终要的部件是ApplicationContext辅助类以及beanFactory,ApplicationContext负责spring配置文件的加载和解析,将bean配置解析转换成一个个BeanDefinition并传递给bean工厂,bean工厂调度ApplicationContext辅助类的解析工作,并在解析完成后负责bean对象实例化。

obtainFreshBeanFactory方法完成工厂创建和刷新,工厂刷新工作内容主要是调度ApplicationContex将bean配置解析转换成一个个BeanDefinition,具体的配置解析和加载工作实现在不同的ApplicationContext实现类,例如xml解析在XmlWebApplicationContext中定义。

org.apache.xbean.spring.context.XmlWebApplicationContext中的配置加载如下:

通过调用父类org.springframework.web.context.support.XmlWebApplicationContext的loadBeanDefinitions实际完成加载工作,其实所有ApplicationContext的对应的配置加载实现都在org.springframework.web.context.support包下面:

org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver负责加载spring配置文件解析器信息配置

扩展NameSpaceHandler

DefaultNamespaceHandlerResolver默认从classpath中META-INF/spring.handler文件中读取配置文件解析器映射关系
spring.handler中的配置如下:

其中配置了配置文件命名空间和NamespaceHandler类的映射关系。

NamespaceHandler中定义了不同类型xml节点的解析器。容器启动过程中XmlWebApplicationContext会通过XmlBeanDefinitionReader读取Spring的配置文件,在解析每一个配置节点时,会从NamespaceHandler中获取对应的解析器进行解析并转换成Spring容器中的实例或者spring配置对象。

Spring源码阅读-spring启动的更多相关文章

  1. (转) Spring源码阅读 之 Spring整体架构

    标签(空格分隔): Spring 声明:本文系转载,原地地址:spring framework 4 源码阅读 Spring骨架 Spring的骨架,也是Spring的核心包.主要包含三个内容 cont ...

  2. Spring源码阅读-ApplicationContext体系结构分析

    目录 继承层次图概览 ConfigurableApplicationContext分析 AbstractApplicationContext GenericApplicationContext Gen ...

  3. Bean实例化(Spring源码阅读)-我们到底能走多远系列(33)

    我们到底能走多远系列(33) 扯淡: 各位:    命运就算颠沛流离   命运就算曲折离奇   命运就算恐吓着你做人没趣味   别流泪 心酸 更不应舍弃   ... 主题: Spring源码阅读还在继 ...

  4. 初始化IoC容器(Spring源码阅读)

    初始化IoC容器(Spring源码阅读) 我们到底能走多远系列(31) 扯淡: 有个问题一直想问:各位你们的工资剩下来会怎么处理?已婚的,我知道工资永远都是不够的.未婚的你们,你们是怎么分配工资的? ...

  5. Sping学习笔记(一)----Spring源码阅读环境的搭建

    idea搭建spring源码阅读环境 安装gradle Github下载Spring源码 新建学习spring源码的项目 idea搭建spring源码阅读环境 安装gradle 在官网中下载gradl ...

  6. Spring源码阅读笔记02:IOC基本概念

    上篇文章中我们介绍了准备Spring源码阅读环境的两种姿势,接下来,我们就要开始探寻这个著名框架背后的原理.Spring提供的最基本最底层的功能是bean容器,这其实是对IoC思想的应用,在学习Spr ...

  7. Spring源码阅读 之 配置的读取,解析

    在上文中我们已经知道了Spring如何从我们给定的位置加载到配置文件,并将文件包装成一个Resource对象.这篇文章我们将要探讨的就是,如何从这个Resouce对象中加载到我们的容器?加载到容器后又 ...

  8. 搭建 Spring 源码阅读环境

    前言 有一个Spring源码阅读环境是学习Spring的基础.笔者借鉴了网上很多搭建环境的方法,也尝试了很多,接下来总结两种个人认为比较简便实用的方法.读者可根据自己的需要自行选择. 方法一:搭建基础 ...

  9. Spring源码阅读总结(Ing)

    一.Spring源码架构 Spring源码地址 二.Spring中的设计模式 1.工厂模式 BeanFactory 2.模板模式 模板的使用者只需设计一个具体的类,集成模板类,然后定制那些具体方法,这 ...

随机推荐

  1. JAVA设计模式--装饰器模式

    装饰器模式 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰 ...

  2. 数据处理框架:Pig

    Pig pig 是基于hadoop的一个数据处理框架. MapReduce是使用java开发的.Pig有一套自己的数据处理语言.Pig的数据处理过程要转化为MR 来运行. Pig的数据处理语言是数据流 ...

  3. Ceph常见问题百科全书

    Ceph是目前炙手可热的一个统一分布式存储系统,具有优异的性能.可靠性.可扩展性.其可轻松扩展到数 PB 容量, 支持多种工作负载的高性能(每秒输入/输出操作[IOPS]和带宽),具有极其高的可靠性. ...

  4. redis学习系列——redis持久化

    1.写操作的流程 2.RDB快照-redis的第一个持久化策略 第一种是以快照的形式持久化到本地磁盘(RDB文件). 持久化策略是: 1.配置(save N M)在N秒内,redis至少发生M次修改, ...

  5. linux 环境下安装oracle11g方法及安装过程中遇上的问题解决方法

     Oralce安装教程 1.先安装需要的依赖包 找到哪个没有安装,直接yum install XX,直到所有的都安装完成.注意,可能一个包安装了,再次执行检查,还是提示没有安装,那么就不需要管他们了, ...

  6. 温故而知新----stack

    stack:栈,先进后出,操作方法相对其它容器来说比较少,具有以下特性:1.LIFO 后进先出,与队列相反,队列时FIFO(先进先出)2.没有迭代器访问.3.C++ 11标准中新增了两个接口,如下:  ...

  7. 聊聊Docker

    为什么是Docker 进入21世纪,继互联网之后,云计算开始大放异彩.云计算是互联网发展后期的必然方向,反过来,云计算也进一步推动了互联网的发展.云计算模式最关键的突破就是资源使用方式的改变. 云计算 ...

  8. IPFS如何挖矿<Filecoin系统>?(一)

    本来这篇文章应该晚一点写, 但是这几天一直有朋友在公众号留言, 迫切的想知道IPFS到底如何挖矿, 所以就提前写一篇关于IPFS挖矿的文章. 本文暂不涉及具体的技术细节, 只做大概的介绍. 首先, 好 ...

  9. 关于Maven的配置与学习

    1. 简介 官方说法:Apache Maven is a software project management and comprehension tool. Based on the concep ...

  10. 笔记:Hibernate SQL 查询

    Hibernate 支持使用原生的SQL查询,使用原生SQL查询可以利用某些数据库特性,原生SQL查询也支持将SQL语句放在配置文件中配置,从而提高程序的解耦,命名SQL查询还可以用于调用存储过程. ...