Spring源码深度解析01-debug式看如何加载xml配置文件

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
简介: Spring源码深度解析01-debug式看如何加载xml配置文件

加载xml配置文件


mian()入口

//读取xx.xml文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("xx.xml");

ClassPathXmlApplicationContext

public ClassPathXmlApplicationContext(
      String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
      throws BeansException {
   //1.调用父类构造方法,初始化一些成员属性
   super(parent);
   //2.设置配置路径
   setConfigLocations(configLocations);
   if (refresh) {
       //3.大名鼎鼎的refresh方法
      refresh();
   }
}


点击diagrams图 先挂着待会有用

1687241951812.jpg

设置配置路径

AbstractRefreshableConfigApplicationContext

public void setConfigLocations(@Nullable String... locations) {
   if (locations != null) {
      Assert.noNullElements(locations, "Config locations must not be null");
      this.configLocations = new String[locations.length];
      for (int i = 0; i < locations.length; i++) {
          //resolvePath 解析路径
         this.configLocations[i] = resolvePath(locations[i]).trim();
      }
   }
   else {
      this.configLocations = null;
   }
}

此时,configLocations已经放在 AbstractRefreshableConfigApplicationContext 待会用到

resolvePath里面的getEnvironment().resolveRequiredPlaceholders(path),这里会寻找Environment map中替换字符,如${name}

如果environment为空,则创建environment。return new StandardEnvironment();

读取BeanDefinitionsrefresh();先跳过prepareRefresh

进去obtainFreshBeanFactory()

进去refreshBeanFactory()

看diagrams图


AbstractApplicationContext的子类

AbstractRefreshableConfigApplicationContext

/**
 * 省略的注解,记得看
 */
@Override
protected final void refreshBeanFactory() throws BeansException {
   if (hasBeanFactory()) {
      destroyBeans();
      closeBeanFactory();
   }
   try {
      //创建BeanFactory DefaultListableBeanFactory重点,之后会经常看到
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      //设置序列化id
      beanFactory.setSerializationId(getId());
      //自定义或定制beanFactory,设置相关Property
      customizeBeanFactory(beanFactory);
      //这节重点 读取BeanDefinitions
      loadBeanDefinitions(beanFactory);
      this.beanFactory = beanFactory;
   }
   catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
   }
}


看diagrams图

AbstractRefreshableConfigApplicationContext

的子类 AbstractXmlApplicationContext

别走散了

/**
 * 省略的注解,记得看
 */
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // Create a new XmlBeanDefinitionReader for the given BeanFactory.
   XmlBeanDefinitionReader beanDefinitionReader = new      XmlBeanDefinitionReader(beanFactory);
   // Configure the bean definition reader with this context's
   // resource loading environment.
   beanDefinitionReader.setEnvironment(this.getEnvironment());
   beanDefinitionReader.setResourceLoader(this);
   beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
   // Allow a subclass to provide custom initialization of the reader,
   // then proceed with actually loading the bean definitions.
   // 初始化BeanDefinitionReader
   initBeanDefinitionReader(beanDefinitionReader);
   重点 读取BeanDefinitions
   loadBeanDefinitions(beanDefinitionReader);
}
/**
 * 省略的注解,记得看
 */
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
  Resource[] configResources = getConfigResources();
  if (configResources != null) {
    reader.loadBeanDefinitions(configResources);
  }
    //getConfigLocations() 点进去会发现跳到父类,这个就是刚刚setConfigLocations()时候放在父类
  String[] configLocations = getConfigLocations();
  if (configLocations != null) {
            加载loadBeanDefinitions
    reader.loadBeanDefinitions(configLocations);
  }
  }


这里要小心点,会容易乱,需要连续点击loadBeanDefinitions,

记住一开始是在AbstractXmlApplicationContext 我贴出来的代码上,记得看入参,


搜索loadBeanDefinitions 让他持续高亮,准备,开始


AbstractBeanDefinitionReader类,循环把location list 提取单个location

return loadBeanDefinitions(location, null);

点击,通过debug发现是第一个,进去(后面解释关于第2点解释

Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int count = loadBeanDefinitions(resources);

又是循环,别急,看入参Resource… resources,


这里会有多个子类,选子类XmlBeanDefinitionReader进去,不懂的先跟我点进去(后面解释)  关于第4点解释


继续点击,恭喜你,进入到真正的xml读取类 XmlBeanDefinitionReader


读取类 XmlBeanDefinitionReader

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
  ...
    // 省略的代码
    ...
   熟悉吧 读取io流 这里一定要会啊
   try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
      InputSource inputSource = new InputSource(inputStream);
      if (encodedResource.getEncoding() != null) {
         inputSource.setEncoding(encodedResource.getEncoding());
      }
      读取完还需要解析xml文件
      return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
   }
   catch (IOException ex) {
      throw new BeanDefinitionStoreException(
            "IOException parsing XML document from " + encodedResource.getResource(), ex);
   ...
   // 省略的代码
   ...
}


以防有人不懂,其实怕我之后自己不记得的ヾ(≧▽≦*)o,


关于第4点


loadBeanDefinitions(XmlBeanDefinitionReader reader) 开局第一句写的就是,如果没有debug第一次看绝对会晕。


关于第2点

建议怼着源码看 防止走散,第2点的


AbstractBeanDefinitionReader 类中loadBeanDefinitions(),建议打个断点,一行行看

public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException

首先,该方法获取资源加载器(ResourceLoader)对象,如果资源加载器为null,则抛出BeanDefinitionStoreException异常,提示无法从指定位置加载Bean定义。


接下来,通过判断资源加载器是否实现了ResourcePatternResolver接口来确定是否支持资源模式匹配。如果支持资源模式匹配,则使用资源加载器的getResources方法获取符合指定位置模式的所有资源。


如果资源加载器不支持资源模式匹配,则假定指定位置为单个资源的绝对URL,并通过资源加载器的getResource方法获取该资源。


在获取资源后,调用loadBeanDefinitions方法重载的形式进行具体的Bean定义加载操作。该方法将返回加载的Bean定义数量。


如果提供了actualResources参数(一个可选的Set<Resource>集合),则将加载的资源添加到该集合中。


最后,根据需要输出日志,表示从指定位置成功加载了多少个Bean定义,并返回加载的Bean定义数量。


总体而言,该方法根据指定的位置加载Bean定义。如果资源加载器支持资源模式匹配,则可以加载符合指定模式的多个资源;否则,仅加载指定位置的单个资源。加载的Bean定义将在后续的处理中用于创建和管理相应的Bean实例。


相关文章
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
76 2
|
1月前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
2天前
|
XML Java 数据格式
使用idea中的Live Templates自定义自动生成Spring所需的XML配置文件格式
本文介绍了在使用Spring框架时,如何通过创建`applicationContext.xml`配置文件来管理对象。首先,在resources目录下新建XML配置文件,并通过IDEA自动生成部分配置。为完善配置,特别是添加AOP支持,可以通过IDEA的Live Templates功能自定义XML模板。具体步骤包括:连续按两次Shift搜索Live Templates,配置模板内容,输入特定前缀(如spring)并按Tab键即可快速生成完整的Spring配置文件。这样可以大大提高开发效率,减少重复工作。
使用idea中的Live Templates自定义自动生成Spring所需的XML配置文件格式
|
2月前
|
Java API Spring
在 Spring 配置文件中配置 Filter 的步骤
【10月更文挑战第21天】在 Spring 配置文件中配置 Filter 是实现请求过滤的重要手段。通过合理的配置,可以灵活地对请求进行处理,满足各种应用需求。还可以根据具体的项目要求和实际情况,进一步深入研究和优化 Filter 的配置,以提高应用的性能和安全性。
|
22天前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
41 2
|
1月前
|
监控 IDE Java
如何在无需重新启动服务器的情况下在 Spring Boot 上重新加载我的更改?
如何在无需重新启动服务器的情况下在 Spring Boot 上重新加载我的更改?
54 8
|
1月前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
67 9
|
2月前
|
XML 数据格式
加载 XML 字符串
加载 XML 字符串
|
2月前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
168 5
|
2月前
|
XML Java 应用服务中间件
tomcat学习一:tomcat 目录及配置文件学习 server.xml 等
这篇文章是关于Apache Tomcat服务器的目录结构、配置文件(特别是server.xml)的详细介绍和学习指南。
109 0
tomcat学习一:tomcat 目录及配置文件学习 server.xml 等