掘金 后端 ( ) • 2024-06-15 23:47

highlight: atelier-cave-dark
theme: channing-cyan

前言

本文是作者写关于Spring源码的第一篇文章,作者水平有限,所有的源码文章仅限用作个人学习记录。文中如有错误欢迎各位留言指正。

上一篇文章分析到对ApplicationContext进行了初始化,接下来看一下初始化之后,对ApplicationContext进行的执行前的一些准备操作。

run

public ConfigurableApplicationContext run(String... args) {
// 为了计时用的,老版本和新版本不一样
   long startTime = System.nanoTime();
   // 初始化一个引导器的上下文,这是属于Spring Boot的上下文。后边还有一个Spring的上下文。apach好喜欢context这个东西,证明写框架这个context是真的好用。
   DefaultBootstrapContext bootstrapContext = createBootstrapContext();
   // 这是Spring的上下文,在这里定义,在下面进行的初始化
   ConfigurableApplicationContext context = null;
   // 配置一个系统属性
   configureHeadlessProperty();
   // 获取配置文件的监听器 重点 也是扩展点,凡是读取配置文件的地方都是扩展点,因为配置在配置文件中的initializer、listener都会在某个阶段被调用
   SpringApplicationRunListeners listeners = getRunListeners(args);
   // 调用监听器发送启动事件,这里可以通过自定义监听器消费这个事件,处理自己的逻辑
   listeners.starting(bootstrapContext, this.mainApplicationClass);
   try {
   // 解析命令行参数 将其封装成一个ApplicationArguments,这个类的变量name被设置成commandLineArgs字符串,变量source是解析args封装的CommandLineArgs对象。
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      // 环境预处理
      ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
      // 配置忽略beanInfo
      configureIgnoreBeanInfo(environment);
      // 打印banner信息
      Banner printedBanner = printBanner(environment);
      // 创建ApplicationContext容器
      context = createApplicationContext();
      // 给上下文设置应用启动对象
      context.setApplicationStartup(this.applicationStartup);
      // 准备应用程序上下文
      prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
      // 刷新应用程序的上下文
      refreshContext(context);
      afterRefresh(context, applicationArguments);
      Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
      if (this.logStartupInfo) {
         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
      }
      listeners.started(context, timeTakenToStartup);
      callRunners(context, applicationArguments);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, listeners);
      throw new IllegalStateException(ex);
   }
   try {
      Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
      listeners.ready(context, timeTakenToReady);
   }
   catch (Throwable ex) {
      handleRunFailure(context, ex, null);
      throw new IllegalStateException(ex);
   }
   return context;
}

refreshContext

refreshContext(context)方法用于刷新应用程序的上下文。它会从配置文件中加载所有的Bean并实例化它们,然后将它们注册到应用程序的上下文中。这个方法通常在应用程序启动时调用,以确保所有的Bean都已经被实例化并准备就绪。

private void refreshContext(ConfigurableApplicationContext context) {
    if (this.registerShutdownHook) {
       shutdownHook.registerApplicationContext(context);
    }
    refresh(context);
}

refresh

刷新给定的ConfigurableApplicationContext上下文。具体来说,它会重新加载上下文中的所有bean,重新初始化它们的属性,并重新创建它们的依赖关系。这在需要重新加载应用程序上下文的情况下非常有用,例如在热部署或重新配置应用程序时。

protected void refresh(ConfigurableApplicationContext applicationContext) {
    applicationContext.refresh();
}

refresh

重载的refresh方法,它在初始化或刷新Spring应用程序上下文时调用。如果在调用父类的refresh方法时抛出了RuntimeException,它将尝试停止与该上下文关联的WebServer。这是为了确保在发生错误时,Web服务器能够安全地停止,而不是导致应用程序崩溃。

public final void refresh() throws BeansException, IllegalStateException {
    try {
       super.refresh();
    }
    catch (RuntimeException ex) {
       WebServer webServer = this.webServer;
       if (webServer != null) {
          webServer.stop();
       }
       throw ex;
    }
}

从这里就可以看出已经开始进入Spring的源码了。

super.refresh()

image.png

今天先简单的看一看这个Spring的源码,后边就开始分析Spring的分析了。这段代码的主要流程如下:

  • 同步代码块:通过synchronized关键字同步代码块,确保应用上下文的刷新是线程安全的。

  • 准备刷新:调用prepareRefresh方法,为应用上下文的刷新做准备。

  • 获取Bean工厂:调用obtainFreshBeanFactory方法,获取一个新的Bean工厂。

  • 准备Bean工厂:调用prepareBeanFactory方法,对Bean工厂进行必要的配置和准备。

  • Bean工厂后处理:调用postProcessBeanFactory方法,允许子类对Bean工厂进行后处理。

  • Bean工厂Post-Processors:调用invokeBeanFactoryPostProcessors方法,执行注册在Bean工厂中的Bean工厂后处理器。

  • Bean Post-Processors:调用registerBeanPostProcessors方法,注册Bean后处理器。

  • 初始化Message Source:调用initMessageSource方法,初始化消息源。

  • 初始化Application Event Multicaster:调用initApplicationEventMulticaster方法,初始化应用事件广播器。

  • 子类特定初始化:调用onRefresh方法,允许子类进行特定的初始化操作。

  • 注册监听器:调用registerListeners方法,注册应用监听器。

  • 初始化Bean:调用finishBeanFactoryInitialization方法,实例化所有非懒加载的单例Bean。

  • 完成刷新:调用finishRefresh方法,完成刷新操作,并发布对应的事件。

  • 异常处理:在发生BeansException异常时,销毁已创建的单例Bean,重置刷新标志,并将异常抛出。

  • 最终代码块:重置一些常用的元数据缓存,释放资源,并结束应用上下文刷新的监控步骤。

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
       StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

       // 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);

          StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
          // Invoke factory processors registered as beans in the context.
          invokeBeanFactoryPostProcessors(beanFactory);

          // Register bean processors that intercept bean creation.
          registerBeanPostProcessors(beanFactory);
          beanPostProcess.end();

          // 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();
       }

       catch (BeansException ex) {
          if (logger.isWarnEnabled()) {
             logger.warn("Exception encountered during context initialization - " +
                   "cancelling refresh attempt: " + ex);
          }

          // Destroy already created singletons to avoid dangling resources.
          destroyBeans();

          // Reset 'active' flag.
          cancelRefresh(ex);

          // Propagate exception to caller.
          throw ex;
       }

       finally {
          // Reset common introspection caches in Spring's core, since we
          // might not ever need metadata for singleton beans anymore...
          resetCommonCaches();
          contextRefresh.end();
       }
    }
}

OK 今天先到这里吧。

See you next time :)