ToB企服应用市场:ToB评测及商务社交产业平台

标题: springboot启动流程 (1) 流程概览 [打印本页]

作者: 天空闲话    时间: 2023-6-15 10:02
标题: springboot启动流程 (1) 流程概览
本文将通过阅读源码方式分析SpringBoot应用的启动流程,不涉及Spring启动部分(有相应的文章介绍)。
本文不会对各个流程做展开分析,后续会有文章介绍详细流程。
SpringApplication类

应用启动入口

使用以下方式启动一个SpringBoot应用:
  1. @SpringBootApplication
  2. public class SpringBootDemoApplication {
  3.   public static void main(String[] args) {
  4.     SpringApplication.run(SpringBootDemoApplication.class, args);
  5.   }
  6. }
复制代码
run方法
  1. public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
  2.         return run(new Class<?>[] { primarySource }, args);
  3. }
  4. public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
  5.         return new SpringApplication(primarySources).run(args);
  6. }
  7. public ConfigurableApplicationContext run(String... args) {
  8.         StopWatch stopWatch = new StopWatch();
  9.         stopWatch.start();
  10.         ConfigurableApplicationContext context = null;
  11.         Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
  12.         configureHeadlessProperty();
  13.         SpringApplicationRunListeners listeners = getRunListeners(args);
  14.         listeners.starting();
  15.         try {
  16.                 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
  17.                 // 获取应用env
  18.                 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
  19.                 configureIgnoreBeanInfo(environment);
  20.                 // 打印banner
  21.                 Banner printedBanner = printBanner(environment);
  22.                 // 创建ApplicationContext
  23.                 context = createApplicationContext();
  24.                 exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
  25.                                 new Class[] { ConfigurableApplicationContext.class }, context);
  26.                 // 一些准备工作
  27.                 prepareContext(context, environment, listeners, applicationArguments, printedBanner);
  28.                 // refresh ApplicationContext
  29.                 refreshContext(context);
  30.                 afterRefresh(context, applicationArguments);
  31.                 stopWatch.stop();
  32.                 if (this.logStartupInfo) {
  33.                         new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
  34.                 }
  35.                 // 调用listener
  36.                 listeners.started(context);
  37.                 // 调用ApplicationRunner和CommandLineRunner
  38.                 callRunners(context, applicationArguments);
  39.         } catch (Throwable ex) {
  40.                 handleRunFailure(context, ex, exceptionReporters, listeners);
  41.                 throw new IllegalStateException(ex);
  42.         }
  43.         try {
  44.                 listeners.running(context);
  45.         } catch (Throwable ex) {
  46.                 handleRunFailure(context, ex, exceptionReporters, null);
  47.                 throw new IllegalStateException(ex);
  48.         }
  49.         return context;
  50. }
复制代码
获取应用env
  1. private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
  2.                 ApplicationArguments applicationArguments) {
  3.         // 创建StandardServletEnvironment, 会初始化四个PropertySource:
  4.         // servletConfigInitParams, servletContextInitParams, systemProperties, systemEnvironment
  5.         // 比如-Dserver.port=8888会在systemProperties中
  6.         ConfigurableEnvironment environment = getOrCreateEnvironment();
  7.         // 添加defaultProperties和命令行配置参数即CommandLinePropertySource
  8.         // 通常都没有这两个配置
  9.         configureEnvironment(environment, applicationArguments.getSourceArgs());
  10.         // 使用ConfigurationPropertySourcesPropertySource封装并暴露所有的PropertySource集
  11.         ConfigurationPropertySources.attach(environment);
  12.         // 添加ApplicationEnvironmentPreparedEvent事件并触发multicastEvent加载应用配置文件
  13.         listeners.environmentPrepared(environment);
  14.         // 将spring.main.xx配置加载到SpringApplication对象
  15.         bindToSpringApplication(environment);
  16.         if (!this.isCustomEnvironment) {
  17.                 environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
  18.                                 deduceEnvironmentClass());
  19.         }
  20.         ConfigurationPropertySources.attach(environment);
  21.         return environment;
  22. }
复制代码
加载配置文件的入口在ConfigFileApplicationListener类中:
  1. public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
  2.         addPropertySources(environment, application.getResourceLoader());
  3. }
  4. protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
  5.         // 添加RandomValuePropertySource
  6.         RandomValuePropertySource.addToEnvironment(environment);
  7.         // 加载配置文件
  8.         new Loader(environment, resourceLoader).load();
  9. }
复制代码
加载配置文件的源码较多,此处不做记录,简单梳理一下流程:
创建ApplicationContext
  1. protected ConfigurableApplicationContext createApplicationContext() {
  2.         Class<?> contextClass = this.applicationContextClass;
  3.         if (contextClass == null) {
  4.                 try {
  5.                         switch (this.webApplicationType) {
  6.                         case SERVLET:
  7.                                 // AnnotationConfigServletWebServerApplicationContext类
  8.                                 contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
  9.                                 break;
  10.                         case REACTIVE:
  11.                                 // AnnotationConfigReactiveWebServerApplicationContext类
  12.                                 contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
  13.                                 break;
  14.                         default:
  15.                                 // AnnotationConfigApplicationContext类
  16.                                 contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
  17.                         }
  18.                 } catch (ClassNotFoundException ex) {
  19.                         throw new IllegalStateException("Unable create a default ApplicationContext", ex);
  20.                 }
  21.         }
  22.         return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
  23. }
复制代码


prepareContext
  1. private void prepareContext(
  2.                 ConfigurableApplicationContext context, ConfigurableEnvironment environment,
  3.                 SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments,
  4.                 Banner printedBanner) {
  5.         context.setEnvironment(environment);
  6.         postProcessApplicationContext(context);
  7.         // Apply any ApplicationContextInitializers to the context before it is refreshed.
  8.         // ApplicationContextInitializers集是在创建SpringApplication对象的时候初始化的
  9.         applyInitializers(context);
  10.         // 触发contextPrepared事件
  11.         listeners.contextPrepared(context);
  12.         if (this.logStartupInfo) {
  13.                 logStartupInfo(context.getParent() == null);
  14.                 logStartupProfileInfo(context);
  15.         }
  16.         // 获取BeanFactory并注册必要的Bean
  17.         ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
  18.         // 注册启动参数
  19.         beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
  20.         // 注册banner printer
  21.         if (printedBanner != null) {
  22.                 beanFactory.registerSingleton("springBootBanner", printedBanner);
  23.         }
  24.         // 设置是否允许Bean覆盖,使用spring.main.allowBeanDefinitionOverriding参数配置
  25.         if (beanFactory instanceof DefaultListableBeanFactory) {
  26.                 ((DefaultListableBeanFactory) beanFactory)
  27.                                 .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
  28.         }
  29.         if (this.lazyInitialization) {
  30.                 context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
  31.         }
  32.         // Load the sources
  33.         Set<Object> sources = getAllSources();
  34.         // 将SpringApplication.run(Xxx.class, args)方法传入的Class注册到容器
  35.         // 使用AnnotatedBeanDefinitionReader.register(Class<?>...)方法注册启动类
  36.         load(context, sources.toArray(new Object[0]));
  37.         // 触发contextLoaded事件
  38.         listeners.contextLoaded(context);
  39. }
复制代码
refreshApplicationContext
  1. protected void refresh(ConfigurableApplicationContext applicationContext) {
  2.         applicationContext.refresh();
  3. }
复制代码
调用的是ServletWebServerApplicationContext的refresh方法:
  1. public final void refresh() throws BeansException, IllegalStateException {
  2.         try {
  3.                 super.refresh();
  4.         } catch (RuntimeException ex) {
  5.                 // 关闭web server
  6.                 WebServer webServer = this.webServer;
  7.                 if (webServer != null) {
  8.                         webServer.stop();
  9.                 }
  10.                 throw ex;
  11.         }
  12. }
复制代码
绝大多数的refresh逻辑都在AbstractApplicationContext类里面,ServletWebServerApplicationContext中会在onRefresh阶段创建webServer:
  1. protected void onRefresh() {
  2.         super.onRefresh();
  3.         try {
  4.                 createWebServer();
  5.         } catch (Throwable ex) {
  6.                 throw new ApplicationContextException("Unable to start web server", ex);
  7.         }
  8. }
复制代码
调用ApplicationRunner和CommandLineRunner
  1. private void callRunners(ApplicationContext context, ApplicationArguments args) {
  2.         List<Object> runners = new ArrayList<>();
  3.         runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
  4.         runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
  5.         AnnotationAwareOrderComparator.sort(runners);
  6.         for (Object runner : new LinkedHashSet<>(runners)) {
  7.                 if (runner instanceof ApplicationRunner) {
  8.                         callRunner((ApplicationRunner) runner, args);
  9.                 }
  10.                 if (runner instanceof CommandLineRunner) {
  11.                         callRunner((CommandLineRunner) runner, args);
  12.                 }
  13.         }
  14. }
复制代码
SpringBootApplication注解

指示一个配置类,该类声明一个或多个@Bean方法,并触发自动配置和组件扫描。这是一个方便的注解,相当于声明@Configuration、@EnableAutoConfiguration和@ComponentScan注解。
  1. @SpringBootConfiguration
  2. @EnableAutoConfiguration
  3. @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
  4.                 @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
  5. public @interface SpringBootApplication {
  6.         /**
  7.          * Exclude specific auto-configuration classes such that they will never be applied.
  8.          */
  9.         @AliasFor(annotation = EnableAutoConfiguration.class)
  10.         Class<?>[] exclude() default {};
  11.         /**
  12.          * Exclude specific auto-configuration class names such that they will never be
  13.          * applied.
  14.          */
  15.         @AliasFor(annotation = EnableAutoConfiguration.class)
  16.         String[] excludeName() default {};
  17.         /**
  18.          * Base packages to scan for annotated components. Use scanBasePackageClasses
  19.          * for a type-safe alternative to String-based package names.
  20.          */
  21.         @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
  22.         String[] scanBasePackages() default {};
  23.         /**
  24.          * Type-safe alternative to scanBasePackages for specifying the packages to
  25.          * scan for annotated components. The package of each class specified will be scanned.
  26.          */
  27.         @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
  28.         Class<?>[] scanBasePackageClasses() default {};
  29.         /**
  30.          * The BeanNameGenerator class to be used for naming detected components
  31.          * within the Spring container.
  32.          */
  33.         @AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
  34.         Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
  35.         /**
  36.          * Specify whether @Bean methods should get proxied in order to enforce
  37.          * bean lifecycle behavior, e.g. to return shared singleton bean instances even in
  38.          * case of direct @Bean method calls in user code. This feature requires
  39.          * method interception, implemented through a runtime-generated CGLIB subclass which
  40.          * comes with limitations such as the configuration class and its methods not being
  41.          * allowed to declare final.
  42.          */
  43.         @AliasFor(annotation = Configuration.class)
  44.         boolean proxyBeanMethods() default true;
  45. }
复制代码
AutoConfigurationPackage注解

Registers packages with AutoConfigurationPackages. When no base packages or base package classes are specified, the package of the annotated class is registered.
  1. @Configuration
  2. public @interface SpringBootConfiguration {
  3.         @AliasFor(annotation = Configuration.class)
  4.         boolean proxyBeanMethods() default true;
  5. }
复制代码
AutoConfigurationImportSelector类

DeferredImportSelector接口的实现类,处理自动装配,导出所有需要自动装配的类。
创建WebServer

SpringBoot会在onRefresh阶段创建webServer,首先从spring容器获取ServletWebServerFactory,然后调用getWebServer方法创建webServer。
getWebServer方法需要传入ServletContextInitializer集来初始化ServletContext。
  1. @AutoConfigurationPackage
  2. @Import(AutoConfigurationImportSelector.class)
  3. public @interface EnableAutoConfiguration {
  4.         /**
  5.          * Exclude specific auto-configuration classes such that they will never be applied.
  6.          */
  7.         Class<?>[] exclude() default {};
  8.         /**
  9.          * Exclude specific auto-configuration class names such that they will never be
  10.          * applied.
  11.          * 当类路径下没有指定的类时,可以使用这个属性指定排除的类
  12.          */
  13.         String[] excludeName() default {};
  14. }
复制代码
我们开发者如果需要使用ServletContextInitializer来初始化ServletContext的话,也可以编写一个实现类,然后将其注册到spring容器即可。
另外,SpringBoot还会自动装配DispatcherServletAutoConfiguration类,这个类会创建DispatcherServlet和DispatcherServletRegistrationBean。DispatcherServlet是SpringWebMvc的最核心组件,DispatcherServletRegistrationBean实现了ServletContextInitializer接口,可以将DispatcherServlet注册到ServletContext。以TomcatServletWebServerFactory为例,这个类会通过TomcatStarter来调用所有的ServletContextInitializer,TomcatStarter实现了ServletContainerInitializer接口,Tomcat的ServletContext在启动阶段会调用ServletContainerInitializer的onStartup方法来初始化Servlet容器。
SpringBoot启动流程


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4