applicationContext文件加载和bean注册流程

​ Spring对于从事Java开发的boy来说,再熟悉不过了,对于我们这个牛逼的框架的介绍就不在这里复述了,Spring这个大杂烩,怎么去使用怎么去配置,各种百度谷歌都能查到很多大牛教程,但是,当我们按着教程一步步的把spring的开发框架搭建起来的时候,有没有一种想搞明白spring的冲动,万事开头难,就要从开头开始,而我认为spring开头就是如何加载配置文件,并初始化配置文件里面的bean当然也包括了我们用注解Service、Component等注解注解的bean,spring在容器启动的时候就要去加载这些内容,然后统一管理这些bean(统一管理的是他们的bean definition),这也就是spring的一个重要概念bean的容器。

​ applicationContext.xml到底是如何加载的呢?我把他简化成以下流程,当然了每个环节里Spring的实现都是错综复杂的,也是很佩服写Spring的大神。

Spring初始化

​ 当我们初学Spring的教程的时候,教程里面肯定会有这样的一步操作,就是新建一个applicationContext.xml文件,当然了这是Spring里必须要有的一个文件,在这个文件里面我们可以进行bean的配置等等工作,让Spring来管理我们的Bean。然后,这个文件放在哪里也是个比较讲究的事情,可能对于初学者来说可额能会往WEB-INF文件夹一放就了事了,确实这样是可以的,因为Spring默认的位置就是这个,但是我们一般不这么做,一般会把这个文件放在resource里面,那这样子做的话,你就要指定位置,让Spring知道你这个文件的位置,这就有了下面一段代码,我们的Spring项目都会在web.xml配置这样的代码:

contextConfigLocationclasspath:applicationContext.xml

那问题来了,当项目启动的时候,spring是怎么去初始化应用的上下文的呢?答案就在类ContextLoader.java里面。当Tomcat启动时候会调用该类里面的一个方法public WebApplicationContext initWebApplicationContext(ServletContext servletContext),这个方法主要完成,根据我们在web.xml里面配置的contextConfigLocation初始化spring的web的应用上下文。具体看下改方法的实现(非完整代码,PS:由于太长了):

public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {...... this.context = createWebApplicationContext(servletContext);//主要代码,创建web应用上下文  ...... configureAndRefreshWebApplicationContext(cwac, servletContext);//配置参数并调用初始化方法 ...... }

在这个方法里面有两句重要代码,第一句createWebApplicationContext(servletContext),这个会根据你配置的contextClass创建一个WebApplicationContext对象,但是我们一般不会配置这个参数,所以Spring默认会创建一个XMLWebApplicationContext对象,而这个就是后续操作的的重要对象,然后接下来一句重要代码configureAndRefreshWebApplicationContext(cwac, servletContext)这个就会去读取我们在web.xml里面配置的参数并set到变量里头去,这样Spring就能找到我们项目的applicationContext.xml文件了,到底如何找到下面会讲。接下来我们来看下configureAndRefreshWebApplicationContext方法的实现如下:

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {if (ObjectUtils.identityToString(wac).equals(wac.getId())) {// The application context id is still set to its original default value// -> assign a more useful id based on available informationString idParam = sc.getInitParameter(CONTEXT_ID_PARAM);if (idParam != null) {wac.setId(idParam);}else {// Generate default id...wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +ObjectUtils.getDisplayString(sc.getContextPath()));}}wac.setServletContext(sc);String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);if (configLocationParam != null) {wac.setConfigLocation(configLocationParam);}// The wac environment's #initPropertySources will be called in any case when the context// is refreshed; do it eagerly here to ensure servlet property sources are in place for// use in any post-processing or initialization that occurs below prior to #refreshConfigurableEnvironment env = wac.getEnvironment();if (env instanceof ConfigurableWebEnvironment) {((ConfigurableWebEnvironment) env).initPropertySources(sc, null);}customizeContext(sc, wac);wac.refresh();}

在这个方法中我们只要关注两个地方,第一个:

String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);if (configLocationParam != null) {wac.setConfigLocation(configLocationParam);}

这块代码块就是,讲我们配置在web.xml里面的参数set到我们的变量中去。第二个地方就是:

wac.refresh();

调用这个执行后续的加载文件操作等后续操作。

Spring是如何找到applicationContext.xml文件

​ 其实,从refresh到Spring里去查找配置文件路径之间,有很多步骤,这些也都要花点时间去理解的,在这里不展开讲,我们只要知道,XmlWebApplicationContext会委托给XmlBeanDefinitionReader类去解析配置文件,在XmlWebApplicationContext类里面有个方法loadBeanDefinitions如下:

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {String[] configLocations = getConfigLocations();if (configLocations != null) {for (String configLocation : configLocations) {reader.loadBeanDefinitions(configLocation);}}}

欢迎工作一到五年的Java工程师朋友们加入Java程序员开发: 721575865

群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!

该方法就是将一个个的配置文件委托给XmlBeanDefinitionReader去解析配置文件,但是解析之前有句代码String[] configLocations = getConfigLocations();这个就是查找我们的配置的文件的方法,

protected String[] getConfigLocations() {return (this.configLocations != null ? this.configLocations : getDefaultConfigLocations());}

实现很简单,就是我们有配置该位置地址就会去读我们配置的路径,否则就会去读默认的配置文件路径,这就是开篇说到的要是没配置路径也能读取到配置文件,前提就是要跟Spring默认定义好的文件路径及文件名保持一致才行。getDefaultConfigLocations函数的实现也很简单:

/** Default config location for the root context */public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";/** Default prefix for building a config location for a namespace */public static final String DEFAULT_CONFIG_LOCATION_PREFIX = "/WEB-INF/";/** Default suffix for building a config location for a namespace */public static final String DEFAULT_CONFIG_LOCATION_SUFFIX = ".xml";protected String[] getDefaultConfigLocations() {if (getNamespace() != null) {return new String[] {DEFAULT_CONFIG_LOCATION_PREFIX + getNamespace() + DEFAULT_CONFIG_LOCATION_SUFFIX};}else {return new String[] {DEFAULT_CONFIG_LOCATION};}}

如果配置了namespace就会去找这个名字的xml配置文件,如果没有配置就去找默认的配置文件。所以不管如何,这个配置文件是必须在spring项目中的。至此,配置文件基本将完,接下来就是重头戏了,就是解析xml以及xml里面的节点,并注册到spring的bean容器中去。

将xml文件转成Document处理对象

如何将xml转成Document对象,这个也是很复杂的操作,首先将resource读取InputStream流,在将InputStream流包装成InputSource对象,在处理成Document对象,直接上代码:

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {Assert.notNull(encodedResource, "EncodedResource must not be null");if (logger.isInfoEnabled()) {logger.info("Loading XML bean definitions from " + encodedResource.getResource());}Set currentResources = this.resourcesCurrentlyBeingLoaded.get();if (currentResources == null) {currentResources = new HashSet(4);this.resourcesCurrentlyBeingLoaded.set(currentResources);}if (!currentResources.add(encodedResource)) {throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");}try {InputStream inputStream = encodedResource.getResource().getInputStream();//获取流try {InputSource inputSource = new InputSource(inputStream);if (encodedResource.getEncoding() != null) {inputSource.setEncoding(encodedResource.getEncoding());}return doLoadBeanDefinitions(inputSource, encodedResource.getResource());}finally {inputStream.close();}}catch (IOException ex) {throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), ex);}finally {currentResources.remove(encodedResource);if (currentResources.isEmpty()) {this.resourcesCurrentlyBeingLoaded.remove();}}}

接下来又到doLoadBeanDefinitions(inputSource, encodedResource.getResource());方法去了,该方法就是生成Doucument对象的,然后就是解析具体的节点了,部分源码如下:

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)throws BeanDefinitionStoreException {Document doc = doLoadDocument(inputSource, resource);//这就是解析成Document对象的操作return registerBeanDefinitions(doc, resource);......}

解析Document不展开讲了,不是本篇的重点,重点是下面的,spring如何解析xml文件的bean及注解的bean然后注册到容器中去,registerBeanDefinitions(doc, resource)是下面的重点。

解析Document里面的节点

XmlBeanDfinitionReader本身又不是直接取解析document的,他是委托给了DefaultBeanDefinitionDocumentReader类去实现,源代码中,会去创建DefaultBeanDefinitionDocumentReader对象实例,然后调用实例的注册方法,代码如下:

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();int countBefore = getRegistry().getBeanDefinitionCount();documentReader.registerBeanDefinitions(doc, createReaderContext(resource));return getRegistry().getBeanDefinitionCount() - countBefore;}

首先,我们必须知道,spring的xml文件里面有两种类型的节点,一种是默认节点,相对于默认节点之外的节点统称自定义节点,这可以从源码里面知道,而默认节点有以下几个:beans、import、alias、bean这几个节点是默认节点,而相对于这几个节点之外的都是默认节点,applicationContext里面有几个自定义节点,如下:property-placeholder、property-override、annotation-config、component-scan、load-time-weaver、spring-configured、mbean-export、mbean-server,这里面常见的有component-scan等,为什么spring要分成默认和自定义节点呢,是因为自定义节点都有特定的业务,比如component-scan,他是去扫描程序包,加载用注解定义的bean,例如开发中的service等bean,所以这些自定义节点都配备了解析器,这些解析器预先初始化好的,解析到什么节点就去获取相应的解析器去处理相应的业务,自定义节点解析器配置如下:

@Overridepublic void init() {registerBeanDefinitionParser("property-placeholder

有没有code能改xml内容_Spring源码解析-applicationContext.xml加载和bean的注册相关推荐

  1. Spring源码解析-applicationContext.xml加载和bean的注册

    applicationContext文件加载和bean注册流程 ​ Spring对于从事Java开发的boy来说,再熟悉不过了,对于我们这个牛逼的框架的介绍就不在这里复述了,Spring这个大杂烩,怎 ...

  2. spring 源码深度解析_spring源码解析之SpringIOC源码解析(下)

    前言:本篇文章接SpringIOC源码解析(上),上一篇文章介绍了使用XML的方式启动Spring,介绍了refresh 方法中的一些方法基本作用,但是并没有展开具体分析.今天就和大家一起撸一下ref ...

  3. mybatis源码解析一 xml解析(解析器)

    最近闲来无事,看着一些源码类的书籍,只是光看,好像并不能给自己很好的益处,无法记下来,所以就有了这个Mybatis源码解析系列的博客.网上也有大量的源码解析,在此记录有两个原因,一是为了加深自己的印象 ...

  4. spring boot2.x设置session有效时间_Spring 源码解析 Scopes 之 Request 、Session 、Application...

    (给ImportNew加星标,提高Java技能) 转自:开源中国,作者:麦克斯 链接:my.oschina.net/wang5v/blog/3017934 Request.Session.Applic ...

  5. [转]/tomcat/conf/server.xml配置文件的源码解析

    备注: 在把Java项目直接放到/tomcat/webapps目录下时,server.xml的代码是不变的,端口为8080 你可以通过修改这个8080端口进行配置,以及配置<host>里面 ...

  6. 一个mapper接口有多个mapper.xml 文件_MyBatis 源码解析:映射文件的加载与解析(上)

    上一篇我们分析了配置文件的加载与解析过程,本文将继续对映射文件的加载与解析实现进行分析.MyBatis 的映射文件用于配置 SQL 语句.二级缓存,以及结果集映射等,是区别于其它 ORM 框架的主要特 ...

  7. 一个做耽美漫画的内容网站源码解析过程,讲解他的框架和功能实现

    这里讲的是43321漫画网源码,采用的是thinkphp6开发,Nginx构架 php7.0环境和mysql应用数据库 漫画站重要的是前后端分离和内容管理系统 而thinkphp拥有强大的控制器和模板 ...

  8. 跟着小马哥学系列之 Spring AOP(基于 XML 定义 Advice 源码解析)

    学好路更宽,钱多少加班. --小马哥 简介 大家好,我是小马哥成千上万粉丝中的一员!2019年8月有幸在叩丁狼教育举办的猿圈活动中知道有这么一位大咖,从此结下了不解之缘!此系列在多次学习极客时间< ...

  9. java验证xml格式是否正确的是_spring源码附录(1)java实现对XML格式的验证

    最近在看spring源码,涉及到xml文档的解析.xml文档的格式验证,发现自己对xml解析的基础较为薄弱,本篇博客复习下DOM方式解析xml(即spring解析xml的方式). DOM解析XML是将 ...

最新文章

  1. Android 软键盘按键监控
  2. 配置Apache 2.2+PHP 5.2.9支持OCI通过Oracle9i Client连接Oracle
  3. python资料txt下载-python全教程下载-哪里有Python教程txt下载
  4. JAVA常用设计模式(一、单例模式、工厂模式)
  5. GCC常用选项使用详解
  6. 最新2019 蚂蚁金服4面(Java)面试题
  7. CMU Deep Learning 2018 by Bhiksha Raj 学习记录(8)
  8. 基于JAVA+SpringMVC+Mybatis+MYSQL的奖学金评定管理系统
  9. 一文读懂数据平台、大数据平台、数据中台
  10. 笔记︱虚拟变量回归=差异显著(方差分析)+差异量化(系数值)
  11. MySQL基础操作命令
  12. C++高级进阶 第二季:mutable 关键字
  13. NEFU 大一寒假训练六(二分查找)2020.01.05
  14. 天猫行业资深店长认证
  15. Python文件的读写以及操作excel
  16. 12张图带你轻松了解 calico 6种场景下宿主机和pod互访的iptables规则链流转情况【下】
  17. 延长SQLyog试用期
  18. 无线路由器的DNS服务器怎么设置,无线路由器dns服务器怎么设置
  19. oracle 常见问题
  20. NVIDIA NX刷机,配置深度学习环境

热门文章

  1. 使用Spring Session和JDBC DataStore进行会话管理
  2. 新闻发布系统java ee_Java EE 7发布–反馈和新闻报道
  3. 与Maven的集成测试
  4. jhsdb:JDK 9的新工具
  5. jaxb 处理_休息使用Jersey –包含JAXB,异常处理和客户端程序的完整教程
  6. osgi java_使普通的旧Java OSGi兼容
  7. 忽略已检查的异常,所有出色的开发人员都在这样做–基于600,000个Java项目
  8. 新电子书:解决生产中Java应用程序错误的完整指南
  9. glassfish5_将Glassfish 3连接到外部ActiveMQ 5代理
  10. 官方野生蝇群流口水分数