当前位置: 首页 > news >正文

Spring是如何实现ApplicationContext应用上下文

    • ApplicationContext实现原理
      • 一,对XMl资源文件的解析
      • 二,BeanFactoryPostProcess的自动化执行
      • 三,BeanPostProcess的自动化执行
      • 四,提前初始化单列Bean
      • 五,完整代码

上篇:Spring是如何实现PostProcess容器扩展机制

在前面的章节当中我们实现了SpringIOC的基本功能,但是这还不够,可以看一下下面的列子

@Test  
public void testBeanPostPostProcess() {  // 创建默认的BeanFactory实例  DefaultListableBeanFactory factory = new DefaultListableBeanFactory();  // 创建XML Bean定义读取器,并加载指定的Spring配置文件  XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(factory);  xmlBeanDefinitionReader.loadBeanDefinitions("classpath:spring.xml");  // 向BeanFactory注册自定义的BeanPostProcessor  factory.addBeanPostProcessor(new CustomerBeanPostProcessor());  // 从BeanFactory中获取名为"people"的Bean实例,并打印其内容  People people = (People) factory.getBean("people");  System.out.println(people);  
}

在这里我们创建了一个BeanFactory对象之后还需要,手动调用方法加载资源文件,同时对于PostProcess也需要我们通过内置方法进行添加,那能否实现自动化的添加就是ApplicationContext要做的事情

ApplicationContext实现原理

其实ApplicationContext的实现与BeanFactory非常类似,都是通过对子类的委托实现功能的拓展。

再说实现ApplicationContext代码之前,我们先想想要做什么,如何实现自动化的对Bean的初始化,要经过哪些步骤

首先我们在初识Spring的时候肯定都了解过一个类ClassPathXmlApplicationContext可以看一下下面的代码

	@Testpublic void testApplicationContext() throws Exception {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");Person person = applicationContext.getBean("person", Person.class);}

我们通过ClassPathXmlApplicationContext这个类的构造函数传入要加载的xml文件从classPath路径,之后我们就可以从容器当中获取到我们的Bean了

那么在整个历程要经过哪些步骤呢

  1. 通过ClassLoader加载xml资源文件,获取到Resource对象,然后将其通过XmlBeanDefinitionReader将里面的标签注册为BeanDefinition。
  2. 执行BeanFactoryPostProcess
  3. 将BeanPostProcess加入到对应的容器当中
  4. 提前初始化Bean

一,对XMl资源文件的解析

在Spring当中对XML文件的加载委托给了XmlBeanDefinitionReader这个类,实际上对于BeanDefinitionReader当中就提供了基本的加载资源文件的接口,其抽象实现AbstractBeanDefinitionReader,对当中的基本逻辑进行了补充。在此之后我们就可以自定义不同文件的加载逻辑去加载为BeanDefinition,值得说一下的是BeanDefinitionReader的加载beanDefinition的方法loadBeanDefinitions在实际外部调用的过程当中传入的是资源文件的ClassPath路径,因此在这里还需要依赖到ResourceLoader去通过解析路径。

上面是对XML文件加载的回顾,经过上述我们也可以明白,要实现自动化的文件加载需要依赖到哪些类,现在我们只需要直接拿出来用即可

来看一下实现逻辑

首先我们得定义好我的ApplicationContext,它也就是一个接口实现逻辑与BeanFactory,都将单一职责的原则贯彻到底,具体的功能拓展都委托给子类

/**  * ApplicationContext 接口是 Spring 框架中的核心接口之一,用于提供应用程序的配置信息。  * 它继承了 ListableBeanFactory、HierarchicalBeanFactory 和 ResourceLoader 接口,  * 从而具备了以下功能:  * 1. 列出所有 Bean 定义的能力(通过 ListableBeanFactory)。  * 2. 支持 Bean 工厂的层次结构(通过 HierarchicalBeanFactory)。  * 3. 加载资源文件的能力(通过 ResourceLoader)。  *  * 该接口通常用于在 Spring 应用程序中获取 Bean 实例、管理 Bean 的生命周期以及加载资源。  */  
public interface ApplicationContext extends ListableBeanFactory , HierarchicalBeanFactory  {  }

ConfigurableApplicationContext实现ApplicationContext的配置化功能

/**  * ConfigurableApplicationContext 接口扩展了 ApplicationContext 接口,提供了对应用上下文配置的支持。  * 该接口允许在应用上下文初始化之前或之后进行配置,例如设置父上下文、刷新上下文等操作。  *  * 实现该接口的类通常用于管理 Spring 应用上下文的生命周期和配置。  *  * @see ApplicationContext */public interface ConfigurableApplicationContext extends ApplicationContext {  /**  * 刷新容器。  * 该方法用于重新加载或更新容器中的内容,通常用于在容器状态发生变化时,  * 确保容器内的数据或配置与当前状态保持一致。  * 该方法不接收任何参数,也不返回任何值。  */  void refresh();  
}

AbstractApplicationContext,在上面的介绍当中也说了,首先我们要做的事情就是解析XML文件初始化Bean,所以我们在AbstractApplicationContext当中的refresh方法当中开始对该逻辑进行实现。但是在这里就还会有一个问题,当前我们的需求是解析XML文件,那如果在后续我们相对其进行拓展我想解析JSON注册Bean怎么办。这里也就是为什么在AbstractApplicationContext当中调用的都是抽象方法,在这里我们实际上就只是定义一个模版,规范解析的步骤,至于具体如何实现就需要根据我们的需求了。这样就可以实现不同解析方式的动态扩展,实现高内聚低耦合。

  
/**  * AbstractApplicationContext 是一个抽象类,实现了 ConfigurableApplicationContext 接口。  * 该类提供了应用程序上下文的基本实现,特别是容器的刷新功能。  *  * @author: jixu * @create: 2025-04-19 14:37 */
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {  /**  * 刷新容器。  * 该方法用于重新加载或刷新应用程序上下文中的配置和资源。  * 通常用于在运行时动态更新应用程序的配置。  *  * 该方法是一个抽象方法的实现,具体刷新逻辑由子类提供。  */  @Override  public void refresh() {  // 通过子类创建BeanFactory,同时初始化beanDefinition  refreshBeanFactory();  // 获取到Bean工厂  ConfigurableListableBeanFactory beanFactory = getBeanFactory();  }  protected abstract void refreshBeanFactory();  protected abstract ConfigurableListableBeanFactory  getBeanFactory();}

AbstractRefreshableApplicationContext,在实现完AbstractApplicationContext的逻辑后我们发现在refreshBeanFactory也就是初始化Bean的方法当中有些逻辑是共通的,不管我们定义何种解析方法都需要用到这些东西,那么我们就可以在这里进行进一步的抽取。

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext{  private ConfigurableListableBeanFactory  beanFactory;  @Override  protected void refreshBeanFactory() {  // 创建Bean  DefaultListableBeanFactory beanFactory = createBeanFactory();  // 加载BeanDefinition  loadBeanDefinitions(beanFactory);  this.beanFactory = beanFactory;  }  protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) ;  private DefaultListableBeanFactory createBeanFactory() {  DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();  return defaultListableBeanFactory;  }  @Override  public ConfigurableListableBeanFactory getBeanFactory() {  return beanFactory;  }  public void setBeanFactory(DefaultListableBeanFactory beanFactory) {  this.beanFactory = beanFactory;  }  }

AbstractXmlApplicationContext现在就来到真正的XML文件的解析逻辑了,可以看到在这里我的定义了一个AbstractXmlApplicationContext,那么是不是也可以定义一个AbstractJSONApplicationContext实现AbstractRefreshableApplicationContext的loadBeanDefinitions方法呢,答案是肯定的。但是只做到这一步还是不够的,我们需要解析XML文件就需要获取到文件的路径,这个路径的获取通过上面的示例也能看出来,是通过构造方法创建的,那我我们就还需要一个用于获取路径的类实现AbstractXmlApplicationContext同时实现获取路径的方法

public abstract class AbstractXmlApplicationContext extends AbstractRefreshableApplicationContext{  @Override  protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {  XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);  String[] locations = getConfigLocations();  xmlBeanDefinitionReader.loadBeanDefinitions(locations);  }  // 获取到Classpath下的所有配置资源路径  protected abstract String[] getConfigLocations();  
}

ClassPathApplicationContext

public class ClassPathApplicationContext extends AbstractXmlApplicationContext{  private String[] configLocations;  @Override  protected String[] getConfigLocations() {  return this.configLocations;  }  public ClassPathApplicationContext(String configLocation){  this.configLocations = new String[]{configLocation};  }  public ClassPathApplicationContext(String[] configLocations){  this.configLocations = configLocations;  }  }

到这里我们的第一步对于XML的解析就讲完了,后面应该就是执行BeanPostProcessors与BeanFactoryPostProcess的增强方法了

二,BeanFactoryPostProcess的自动化执行

首先我们先来回顾一下BeanFactoryPostProcess的实现原理,BeanFactoryPostProcessor的实现是基于Spring的一个扩展点ConfigurableListableBeanFactory,在该接口当中我们可以通过其内置的方法getBeanDefinition获取到指定Bean的定义信息之后就可以对其进行修改。除此之外执行增强方法也需要调用有具体子类实现的postProcessBeanFactory方法。

那么现在就会产生一个问题,如何拿到所有已定义的BeanFactoryPostProcess。其实逻辑很简单,获取到所有的BeanFactoryPostProcess,之后循环遍历执行即可。Spring是通过将所有的BeanFactoryPostProcess加入到容器当中,然后从容器获取BeanFactoryPostProcess类型的Bean,来解决这个问题的。

// 执行BeanFactoryPostProcess的方法  
invokeBeanFactoryPostProcessors(beanFactory);
/**  * 执行所有BeanFactoryPostProcessor的postProcessBeanFactory方法。  * 该方法会从BeanFactory中获取所有类型为BeanFactoryPostProcessor的Bean,并依次调用它们的postProcessBeanFactory方法。  *  * @param beanFactory 可配置的BeanFactory实例  */
private void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {  // 获取到所有已注册到容器当中的BeanPostProcess  Map<String, BeanFactoryPostProcessor> beanFactoryPostProcessorMap = beanFactory.getBeanOfType(BeanFactoryPostProcessor.class);  for (BeanFactoryPostProcessor beanFactoryPostProcessor : beanFactoryPostProcessorMap.values()) {  beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);  }  
}

三,BeanPostProcess的自动化执行

BeanPostProcess的逻辑与BeanFactoryPostProcess非常类似,只是区别在于BeanPostProcess是对Bean的增强其前置与后置增强方法不需要我们手动调用,在createBean的时候会自动调用这些方法。我们要做的就是将用户已定义的BeanPostProcess加入到对应的BeanPostProcessMap当中。

// 注册BeanPostPostProcess  
registerBeanPostProcessors(beanFactory);
/**  * 注册所有BeanPostProcessor到BeanFactory中。  * 该方法会从BeanFactory中获取所有类型为BeanPostProcessor的Bean,并将它们注册到BeanFactory中。  *  * @param beanFactory 可配置的BeanFactory实例  */  
private void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {  Map<String, BeanPostProcessor> beanPostProcessorMap = beanFactory.getBeanOfType(BeanPostProcessor.class);  for (BeanPostProcessor beanPostProcessor : beanPostProcessorMap.values()) {  beanFactory.addBeanPostProcessor(beanPostProcessor);  }  
}

四,提前初始化单列Bean

preInstantiateSingletons方法是ConfigurableListableBeanFactory提供的,我们在AbstractApplicationContext当中已经继承了该方法,直接调用即可

// 提前初始化单列Bean  
beanFactory.preInstantiateSingletons();

五,完整代码

可参考 https://gitee.com/jixuonline/jixu-mini-spring.git

在这里插入图片描述

ApplicationContext

/**  * ApplicationContext 接口是 Spring 框架中的核心接口之一,用于提供应用程序的配置信息。  * 它继承了 ListableBeanFactory、HierarchicalBeanFactory 和 ResourceLoader 接口,  * 从而具备了以下功能:  * 1. 列出所有 Bean 定义的能力(通过 ListableBeanFactory)。  * 2. 支持 Bean 工厂的层次结构(通过 HierarchicalBeanFactory)。  * 3. 加载资源文件的能力(通过 ResourceLoader)。  *  * 该接口通常用于在 Spring 应用程序中获取 Bean 实例、管理 Bean 的生命周期以及加载资源。  */  
public interface ApplicationContext extends ListableBeanFactory , HierarchicalBeanFactory  {  
}

ConfigurableApplicationContext

/**  * ConfigurableApplicationContext 接口扩展了 ApplicationContext 接口,提供了对应用上下文配置的支持。  * 该接口允许在应用上下文初始化之前或之后进行配置,例如设置父上下文、刷新上下文等操作。  *  * 实现该接口的类通常用于管理 Spring 应用上下文的生命周期和配置。  *  * @see ApplicationContext */public interface ConfigurableApplicationContext extends ApplicationContext {  /**  * 刷新容器。  * 该方法用于重新加载或更新容器中的内容,通常用于在容器状态发生变化时,  * 确保容器内的数据或配置与当前状态保持一致。  * 该方法不接收任何参数,也不返回任何值。  */  void refresh();  
}

AbstractApplicationContext

/**  * AbstractApplicationContext 是一个抽象类,实现了 ConfigurableApplicationContext 接口。  * 该类提供了应用程序上下文的基本实现,特别是容器的刷新功能。  *  * @author: jixu * @create: 2025-04-19 14:37*/public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {  /**  * 刷新容器。  * 该方法用于重新加载或刷新应用程序上下文中的配置和资源。  * 通常用于在运行时动态更新应用程序的配置。  *  * 该方法是一个抽象方法的实现,具体刷新逻辑由子类提供。  */  @Override  public void refresh() {  // 通过子类创建BeanFactory,同时初始化beanDefinition  refreshBeanFactory();  // 获取到Bean工厂  ConfigurableListableBeanFactory beanFactory = getBeanFactory();  // 执行BeanFactoryPostProcess的方法  invokeBeanFactoryPostProcessors(beanFactory);  // 注册BeanPostPostProcess  registerBeanPostProcessors(beanFactory);  // 提前初始化单列Bean  beanFactory.preInstantiateSingletons();  }  /**  * 注册所有BeanPostProcessor到BeanFactory中。  * 该方法会从BeanFactory中获取所有类型为BeanPostProcessor的Bean,并将它们注册到BeanFactory中。  *  * @param beanFactory 可配置的BeanFactory实例  */  private void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {  Map<String, BeanPostProcessor> beanPostProcessorMap = beanFactory.getBeanOfType(BeanPostProcessor.class);  for (BeanPostProcessor beanPostProcessor : beanPostProcessorMap.values()) {  beanFactory.addBeanPostProcessor(beanPostProcessor);  }  }  /**  * 执行所有BeanFactoryPostProcessor的postProcessBeanFactory方法。  * 该方法会从BeanFactory中获取所有类型为BeanFactoryPostProcessor的Bean,并依次调用它们的postProcessBeanFactory方法。  *  * @param beanFactory 可配置的BeanFactory实例  */  private void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {  // 获取到所有已注册到容器当中的BeanPostProcess  Map<String, BeanFactoryPostProcessor> beanFactoryPostProcessorMap = beanFactory.getBeanOfType(BeanFactoryPostProcessor.class);  for (BeanFactoryPostProcessor beanFactoryPostProcessor : beanFactoryPostProcessorMap.values()) {  beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);  }  }  /**  * 刷新BeanFactory。  * 该方法是一个抽象方法,具体实现由子类提供,用于创建或刷新BeanFactory。  */  protected abstract void refreshBeanFactory();  /**  * 获取当前BeanFactory实例。  * 该方法是一个抽象方法,具体实现由子类提供,用于返回当前BeanFactory的实例。  *  * @return 当前BeanFactory的实例  */  protected abstract ConfigurableListableBeanFactory  getBeanFactory();  /**  * 根据指定的类型获取所有符合条件的Bean实例,并以Map形式返回。  * Map的键为Bean的名称,值为对应的Bean实例。  *  * @param type 要查找的Bean类型  * @return 包含所有符合类型条件的Bean实例的Map,键为Bean名称,值为Bean实例  */  @Override  public <T> Map<String, T> getBeanOfType(Class<T> type) {  return getBeanFactory().getBeanOfType(type);  }  /**  * 获取当前BeanFactory中所有Bean定义的名称。  *  * @return 包含所有Bean定义名称的字符串数组  */  @Override  public String[] getBeanDefinitionNames() {  return getBeanFactory().getBeanDefinitionNames();  }  /**  * 根据指定的 Bean 名称获取对应的 Bean 实例。  *  * @param name Bean 的名称,通常是在 Spring 配置文件中定义的 Bean 的 ID 或名称。  * @return 返回与指定名称对应的 Bean 实例。如果找不到对应的 Bean,可能会抛出异常。  */  @Override  public Object getBean(String name) {  return getBeanFactory().getBean(name);  }  
}

AbstractRefreshableApplicationContext

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext{  private ConfigurableListableBeanFactory  beanFactory;  @Override  protected void refreshBeanFactory() {  // 创建Bean  DefaultListableBeanFactory beanFactory = createBeanFactory();  // 加载BeanDefinition  loadBeanDefinitions(beanFactory);  this.beanFactory = beanFactory;  }  protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) ;  private DefaultListableBeanFactory createBeanFactory() {  DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();  return defaultListableBeanFactory;  }  @Override  public ConfigurableListableBeanFactory getBeanFactory() {  return beanFactory;  }  public void setBeanFactory(DefaultListableBeanFactory beanFactory) {  this.beanFactory = beanFactory;  }  }

AbstractXmlApplicationContext

public abstract class AbstractXmlApplicationContext extends AbstractRefreshableApplicationContext{  @Override  protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {  XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);  String[] locations = getConfigLocations();  xmlBeanDefinitionReader.loadBeanDefinitions(locations);  }  // 获取到Classpath下的所有配置资源路径  protected abstract String[] getConfigLocations();  
}

ClassPathApplicationContext

public class ClassPathApplicationContext extends AbstractXmlApplicationContext{  private String[] configLocations;  @Override  protected String[] getConfigLocations() {  return this.configLocations;  }  public ClassPathApplicationContext(String configLocation){  this.configLocations = new String[]{configLocation};  }  public ClassPathApplicationContext(String[] configLocations){  this.configLocations = configLocations;  }  }

相关文章:

  • 提示词工程实战指南:解锁AI创作的隐藏技巧与实例
  • 大模型在肾癌诊疗全流程中的应用研究报告
  • iOS—仿tableView自定义闹钟列表
  • KUKA机器人关机时冷启动介绍
  • iOS - 音频: Core Audio - 播放
  • Java云原生+quarkus
  • python:sklearn 主成分分析(PCA)
  • Android 手动删除 AAR jar 包 中的文件
  • Weka通过10天的内存指标数据计算内存指标动态阈值
  • Mac 创建QT按钮以及一些操作
  • Kafka的Rebalance机制可能引发什么问题?如何优化?怎么减少不必要的Rebalance
  • 四.割草机技术总结--4.基站发送给流动站的差分数据传输标准RTCM
  • Elasticsearch 内存使用指南
  • milvus编译与使用
  • 日本IT行业|salesforce开发语言占据的地位
  • 【C++11】类的新功能
  • Android——Serializable和Parcelable
  • C++ 如何计算两个gps 的距离
  • Vue3调度器错误解析,完美解决Unhandled error during execution of scheduler flush.
  • ElasticSearch入门
  • 辽宁辽阳市白塔区一饭店火灾事故举行新闻发布会,现场为遇难者默哀
  • 张炜琳已任三明市委常委、宣传部部长
  • 违规行为屡禁不止、责任边界模糊不清,法治日报:洞穴探险,谁为安全事故买单?
  • 气候资讯|4月全球前沿气候科学研究&极端天气气候事件
  • 癌症来临前,可能伪装成这几种常见病,千万别大意
  • 专业竞演、剧场LIVE直播,32位越剧新星逐梦上海