Springboot启动过程源码分析

262 阅读7分钟

又是美好的一天呀~ 个人博客地址: huanghong.top

本文预估阅读时长为30分钟左右~

核心功能

  1. 可独立运行的Spring项目:Spring Boot可以以jar包的形式独立运行。
  2. 内嵌的Servlet容器:Spring Boot可以选择内嵌Tomcat、Jetty或者Undertow,无须以war包形式部署项目。
  3. 简化的Maven配置:Spring提供推荐的基础 POM 文件来简化Maven 配置。
  4. 自动配置Spring:Spring Boot会根据项目依赖来自动配置Spring 框架,极大地减少项目要使用的配置。
  5. 提供生产就绪型功能:提供可以直接在生产环境中使用的功能,如性能指标、应用信息和应用健康检查。
  6. 无代码生成和xml配置:Spring Boot不生成代码。完全不需要任何xml配置即可实现Spring的所有配置。

启动过程源码分析

源码基于Springboot 2.6.6版本

image-20230309224616144

运行 SpringApplication.run()

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @Time 2023-03-09 20:17
 * Created by Huang
 * className: Main
 * Description:
 */
@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        //执行run方法
        SpringApplication.run(Main.class, args);
    }
}

构建SpringApplication对象

//org.springframework.boot.SpringApplication#SpringApplication(org.springframework.core.io.ResourceLoader, java.lang.Class<?>...)
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    //赋值null
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    //设置主启动类
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    //设置应用程序类型
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    //设置启动注册初始化器
    this.bootstrapRegistryInitializers = new ArrayList<>(
        getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    //设置初始化器
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    //设置监听器
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

确定应用程序类型

//org.springframework.boot.WebApplicationType#deduceFromClasspath
static WebApplicationType deduceFromClasspath() {
    //如果org.springframework.web.reactive.DispatcherHandler存在且org.springframework.web.servlet.DispatcherServlet和、、、、
    //org.glassfish.jersey.servlet.ServletContainer不存在,则设置为响应式类型
   if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
         && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
      return WebApplicationType.REACTIVE;
   }
   //javax.servlet.Servlet或org.springframework.web.context.ConfigurableWebApplicationContext不存在,则设置为NONE类型
   for (String className : SERVLET_INDICATOR_CLASSES) {
      if (!ClassUtils.isPresent(className, null)) {
         return WebApplicationType.NONE;
      }
   }
   //默认返回servlet-web类型
   return WebApplicationType.SERVLET;
}

加载所有的初始化器

通过org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories读取META-INF/spring.factories配置文件

org\springframework\boot\spring-boot-autoconfigure\2.6.6\spring-boot-autoconfigure-2.6.6.jar!\META-INF\spring.factories

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

org\springframework\boot\spring-boot\2.6.6\spring-boot-2.6.6.jar!\META-INF\spring.factories

# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

加载Springboot内置初始化器

自定义初始化器

  1. 定义初始化器

    package com.huang.Initializer;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.context.ApplicationContextInitializer;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.stereotype.Component;
    
    /**
     * @Time 2023-03-09 21:11
     * Created by Huang
     * className: DemoInitializer
     * Description:
     */
    @Slf4j
    @Component
    public class DemoInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
        @Override
        public void initialize(ConfigurableApplicationContext applicationContext) {
            log.info("DemoInitializer is running");
            log.info("applicationContext: {}",applicationContext.toString());
        }
    }
    
  2. 在src/main/resources目录下创建META-INF/spring.factories;

    org.springframework.context.ApplicationContextInitializer=com.huang.Initializer.DemoInitializer
    
  3. 启动项目后日志打印

    com.huang.Initializer.DemoInitializer    : DemoInitializer is running
    com.huang.Initializer.DemoInitializer    : applicationContext: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@576f63f6, started on Thu Jan 01 08:00:00 CST 1970
    

加载所有的监听器

通过org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories读取META-INF/spring.factories配置文件

org\springframework\boot\spring-boot-autoconfigure\2.6.6\spring-boot-autoconfigure-2.6.6.jar!\META-INF\spring.factories

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

org\springframework\boot\spring-boot\2.6.6\spring-boot-2.6.6.jar!\META-INF\spring.factories

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener

加载Springboot内置初始化器

自定义监听器

  1. 定义监听器

    package com.huang.listener;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.boot.context.event.ApplicationReadyEvent;
    import org.springframework.context.ApplicationListener;
    import org.springframework.stereotype.Component;
    
    /**
     * @Time 2023-03-09 21:34
     * Created by Huang
     * className: DemoListener
     * Description:
     */
    @Slf4j
    @Component
    public class DemoListener implements ApplicationListener<ApplicationReadyEvent> {
        @Override
        public void onApplicationEvent(ApplicationReadyEvent event) {
            log.info("DemoListener is running");
            log.info("event: {}",event.toString());
        }
    }
    
    
  2. 在src/main/resources目录下创建META-INF/spring.factories;

    org.springframework.context.ApplicationListener=com.huang.listener.DemoListener
    
  3. 启动项目后日志打印

    com.huang.listener.DemoListener          : DemoInitializer is running
    com.huang.listener.DemoListener          : applicationContext: org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent[source=org.springframework.boot.web.embedded.tomcat.TomcatWebServer@47b2e9e1]
    

设置程序运行的主启动类

private Class<?> deduceMainApplicationClass() {
   try {
      StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
      for (StackTraceElement stackTraceElement : stackTrace) {
         if ("main".equals(stackTraceElement.getMethodName())) {
            //返回运行main方法的启动类
            return Class.forName(stackTraceElement.getClassName());
         }
      }
   }
   catch (ClassNotFoundException ex) {
      // Swallow and continue
   }
   return null;
}

run方法

//org.springframework.boot.SpringApplication#run(java.lang.String...)
public ConfigurableApplicationContext run(String... args) {
   //记录启动时间
   long startTime = System.nanoTime();
   //创建启动上下文
   DefaultBootstrapContext bootstrapContext = createBootstrapContext();
   ConfigurableApplicationContext context = null;
   //将java.awt.headless设置为true,表示运行在服务器端,在没有显示器器和鼠标键盘的模式下照样可以工作,模拟输入输出设备功能
   configureHeadlessProperty();
   //创建所有Spring 运行监听器并发布应用启动事件并启用监听器
   SpringApplicationRunListeners listeners = getRunListeners(args);
   listeners.starting(bootstrapContext, this.mainApplicationClass);
   try {
      //将执行run方法时传入的参数封装成一个对象
      ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
      //准备环境变量,包含系统属性和用户配置的属性
      ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
      //将spring.beaninfo.ignore的默认值值设为true,跳过beanInfo的搜索
      configureIgnoreBeanInfo(environment);
      //打印Banner
      Banner printedBanner = printBanner(environment);
      //创建应用程序的上下文
      context = createApplicationContext();
      //为上下文设置applicationStartup,用于记录应用启动期间的metrics
      context.setApplicationStartup(this.applicationStartup);
      //准备上下文环境
      //实例化单例的beanName生成器、执行初始化方法、将启动参数注册到容器中
      prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
      //刷新上下文(自动配置和tomcat容器启动)
      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);
      //执行自定义扩展的run方法
      //实现 ApplicationRunner、CommandLineRunner 接口
      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;
}

初始化启动上下文对象

//org.springframework.boot.SpringApplication#createBootstrapContext
private DefaultBootstrapContext createBootstrapContext() {
   //创建默认启动上下文对象
   DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
   //执行所有org.springframework.boot.BootstrapRegistryInitializer初始化器中初始化方法
   this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));
   //返回上下文实例
   return bootstrapContext;
}

设置headless为true

//org.springframework.boot.SpringApplication#configureHeadlessProperty
private void configureHeadlessProperty() {
   //将java.awt.headless设置为true,表示运行在服务器端,在没有显示器器和鼠标键盘的模式下照样可以工作,模拟输入输出设备功能
   System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
         System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}

获取并启用监听器

//org.springframework.boot.SpringApplication#getRunListeners
private SpringApplicationRunListeners getRunListeners(String[] args) {
   Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
   return new SpringApplicationRunListeners(logger,
         //获取META-INF/spring.factories中SpringApplicationRunListener配置监听器
         getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
         this.applicationStartup);
}

//org.springframework.boot.SpringApplicationRunListeners#starting
//启动监听器
void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
    doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
                    (step) -> {
                        if (mainApplicationClass != null) {
                            step.tag("mainApplicationClass", mainApplicationClass.getName());
                        }
                    });
}

设置应用程序参数

//org.springframework.boot.DefaultApplicationArguments#DefaultApplicationArguments
//将执行run方法时传入的参数(main方法参数)封装成一个对象
public DefaultApplicationArguments(String... args) {
   Assert.notNull(args, "Args must not be null");
   this.source = new Source(args);
   this.args = args;
}

准备环境变量

准备环境变量,包含系统属性和用户配置的属性

//org.springframework.boot.SpringApplication#prepareEnvironment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
      DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
   //创建并配置环境
   ConfigurableEnvironment environment = getOrCreateEnvironment();
   configureEnvironment(environment, applicationArguments.getSourceArgs());
   ConfigurationPropertySources.attach(environment);
   //发布环境准备就绪状态
   listeners.environmentPrepared(bootstrapContext, environment);
   DefaultPropertiesPropertySource.moveToEnd(environment);
   Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
         "Environment prefix cannot be set via properties.");
   //绑定至应用程序
   bindToSpringApplication(environment);
   if (!this.isCustomEnvironment) {
      environment = convertEnvironment(environment);
   }
   ConfigurationPropertySources.attach(environment);
   return environment;
}

忽略bean信息

将 spring.beaninfo.ignore 的默认值值设为true,跳过beanInfo的搜索

//org.springframework.boot.SpringApplication#configureIgnoreBeanInfo
private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
   //spring.beaninfo.ignore
   if (System.getProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {
      Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE);
      System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString());
   }
}

可以通过配置不跳过beanInfo搜索

spring.beaninfo.ignore=false

打印 banner 信息

  • 在resources目录下添加一个自定义banner.txt
  • 图片banner后缀(gif/jpg/png)

创建应用程序的上下文

实例化应用程序的上下文, 调用 createApplicationContext() 方法,这里就是用反射创建对象

//org.springframework.boot.SpringApplication#createApplicationContext
protected ConfigurableApplicationContext createApplicationContext() {
   return this.applicationContextFactory.create(this.webApplicationType);
}

准备上下文环境

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
      ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments, Banner printedBanner) {
   //设置环境
   context.setEnvironment(environment);
   //实例化单例的beanName生成器
   postProcessApplicationContext(context);
   //执行初始化方法
   applyInitializers(context);
   listeners.contextPrepared(context);
   bootstrapContext.close(context);
   if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
   }
   // Add boot specific singleton beans
   ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
   //将启动参数注册到容器中
   beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
   if (printedBanner != null) {
      beanFactory.registerSingleton("springBootBanner", printedBanner);
   }
   if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
      //允许循环应用
      ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
      if (beanFactory instanceof DefaultListableBeanFactory) {
         //允许beanDefinition重写
         ((DefaultListableBeanFactory) beanFactory)
               .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
      }
   }
   //如果是上下文是懒加载,则添加LazyInitializationBeanFactoryPostProcessor扩展点
   if (this.lazyInitialization) {
      context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
   }
   // Load the sources
   Set<Object> sources = getAllSources();
   Assert.notEmpty(sources, "Sources must not be empty");
   load(context, sources.toArray(new Object[0]));
   listeners.contextLoaded(context);
}

实例化单例beanName生成器

//org.springframework.boot.SpringApplication#postProcessApplicationContext
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
   //注册org.springframework.context.annotation.internalConfigurationBeanNameGenerator
   if (this.beanNameGenerator != null) {
      context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
            this.beanNameGenerator);
   }
   //注册资源加载器
   if (this.resourceLoader != null) {
      if (context instanceof GenericApplicationContext) {
         ((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
      }
      if (context instanceof DefaultResourceLoader) {
         ((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
      }
   }
   //设置类型转换服务
   if (this.addConversionService) {
      context.getBeanFactory().setConversionService(context.getEnvironment().getConversionService());
   }
}

执行初始化方法

//org.springframework.boot.SpringApplication#applyInitializers
protected void applyInitializers(ConfigurableApplicationContext context) {
   for (ApplicationContextInitializer initializer : getInitializers()) {
      Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
            ApplicationContextInitializer.class);
      Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
      //执行初始化方法
      initializer.initialize(context);
   }
}

刷新上下文

//org.springframework.boot.SpringApplication#refreshContext
//上下文刷新
private void refreshContext(ConfigurableApplicationContext context) {
   if (this.registerShutdownHook) {
      shutdownHook.registerApplicationContext(context);
   }
   refresh(context);
}

org.springframework.context.support.AbstractApplicationContext#refresh完成上下文刷新

包含beanFactory初始化、beanFactory扩展、bean实例化、事件广播器初始化、监听器注册、Tomcat容器初始化

org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh完成tomcat容器初始化

protected void onRefresh() {
   super.onRefresh();
   try {
      createWebServer();
   }
   catch (Throwable ex) {
      throw new ApplicationContextException("Unable to start web server", ex);
   }
}

刷新上下文后置处理

应用启动后扩展使用

//org.springframework.boot.SpringApplication#afterRefresh
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}

执行自定义的run方法

//org.springframework.boot.SpringApplication#callRunners
private void callRunners(ApplicationContext context, ApplicationArguments args) {
   List<Object> runners = new ArrayList<>();
   runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
   runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
   AnnotationAwareOrderComparator.sort(runners);
   for (Object runner : new LinkedHashSet<>(runners)) {
      if (runner instanceof ApplicationRunner) {
         callRunner((ApplicationRunner) runner, args);
      }
      if (runner instanceof CommandLineRunner) {
         callRunner((CommandLineRunner) runner, args);
      }
   }
}

自定义run方法

  1. 实现 ApplicationRunner 接口

    package com.huang.run;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.boot.ApplicationArguments;
    import org.springframework.boot.ApplicationRunner;
    import org.springframework.stereotype.Component;
    
    /**
     * @Time 2023-03-09 22:37
     * Created by Huang
     * className: DemoApplicationRunner
     * Description:
     */
    @Slf4j
    @Component
    public class DemoApplicationRunner implements ApplicationRunner {
        @Override
        public void run(ApplicationArguments args) throws Exception {
            log.info("DemoApplicationRunner is running");
            log.info("ApplicationArguments: {}",args);
        }
    }
    
  2. 实现 CommandLineRunner 接口

    package com.huang.run;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.boot.CommandLineRunner;
    import org.springframework.stereotype.Component;
    
    /**
     * @Time 2023-03-09 22:38
     * Created by Huang
     * className: DemoCommandLineRunner
     * Description:
     */
    @Slf4j
    @Component
    public class DemoCommandLineRunner implements CommandLineRunner {
        @Override
        public void run(String... args) throws Exception {
            log.info("DemoApplicationRunner is running");
            log.info("ApplicationArguments: {}", String.join(",", args));
        }
    }
    

日志输出

com.huang.run.DemoApplicationRunner      : DemoApplicationRunner is running
com.huang.run.DemoApplicationRunner      : ApplicationArguments: org.springframework.boot.DefaultApplicationArguments@f973499
com.huang.run.DemoCommandLineRunner      : DemoApplicationRunner is running
com.huang.run.DemoCommandLineRunner      : ApplicationArguments: 

感谢阅读完本篇文章!!! 个人博客地址: huanghong.top