SpringBoot启动流程

LOVELETTERD·2023-04-05 22:11·149 次阅读

SpringBoot启动流程

启动类#

Copy
@SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App.class,args); } }

@SpringBootApplication#

对于一个SpringBoot程序的启动首先需要一个加了@SpringBootApplication注解的启动类。

@SpringBootApplication本质上是一个复合注解由三个注解组成

  1. @EnableAutoConfiguration,有了它之后再启动时就会导入“自动配置”AutoConfigurationImportSelector类,这个类会将所有复合条件的@Configuration配置都进行加载。
  2. @SpringBootConfiguration,等同于@Configuration,就是将这个类标记为配置类,会被加载到容器中。
  3. @ComponentScan,自动扫描并加载复合条件的Bean

如果启动类中不需要增加配置内容不需要指定扫描路径,可以用@EnableAutoConfiguration替代@SpringBootApplication

SpringApplication.run(App.class,args);#

注解完成后,我们运行的起点就是SpringApplicationrun方法,再run方法执行后会经历如下四个阶段

  1. 服务构建
  2. 环境准备
  3. 容器创建
  4. 填充容器
  5. 执行Runner

服务构建#

服务构建指的服务就是SpringApplication本身,这个阶段指的就是创建SpringApplication对象。

  1. 首先需要把传入的资源加载器、主方法类记录再内存中

    Copy
    this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources))
  2. 然后逐一判断对应的服务类是否存在来确定Web服务的类型,ServletReactiveNone,默认为Servlet。

    Copy
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
  3. 加载初始化类,通过读取所有META-INF/spring.factories文件中BootstrapRegistryInitializer(注册初始化)、ApplicationContextInitializer(上下文初始化)、ApplicationListener(监听器)这三类配置。SpringBoot中没有默认的初始化配置,但是配置了7个上下文初始化和8个监听器。这些配置信息会在后续的启动中用到,我们也可以自定义这三个配置,只需要放到工程中的spring.factories文件中。

    Copy
    this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories(); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
  4. 判断main方法所在的类,大概率就是启动类本身。

    Copy
    this.mainApplicationClass = deduceMainApplicationClass();

自此我们的Spring服务SpringApplication就构造完成了。

环境准备#

这个阶段的目的是给即将创建的容器配置好环境信息。

  1. 我们会先创建一个后续会用到的启动上下文BootStrapContext,同时逐一调用刚刚加载的"注册初始化器"BootstrapRegistryInitializer中的initialize方法。因为我们默认的没有BootstrapRegistryInitializer所以并不执行什么。

    Copy
    DefaultBootstrapContext bootstrapContext = createBootstrapContext(); private DefaultBootstrapContext createBootstrapContext() { DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext(); this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext)); return bootstrapContext; }
  2. 设置缺少显示器、键盘等输入设备也可以正常启动

    Copy
    configureHeadlessProperty(); private void configureHeadlessProperty() { System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless))); }
  3. 获取并启动'运行监听器'SpringApplicationRunListeners,同时发布启动事件。它获取并加载SpringBoot工程中的spirng.factories配置文件中的EventPublishingRunListener,它在启动时也会将刚刚我们说的8个ApplicationListener都进行引入。

    Copy
    SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(bootstrapContext, this.mainApplicationClass); private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup); }
  4. 通过prepareEnvironment方法组装启动参数。

    Copy
    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); bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; }
    1. 根据不同的Web环境构建不同的环境 ,默认Servlet
    2. 加载systemEnvironment(系统环境变量,比如JAVA_HOME、PATH)systemProperties(jvm系统属性比如java.vm.version、file.encoding)等在内的4组配置信息。把这些配置信息都加载到一个叫做propertySources的内存集合中,方便后续使用就无需重新加载。
    3. 通过configureEnvironment(配置环境)将我们传入的环境参数args进行设置。发布环境准备完成事件
  5. spring.beaninfo.ignore设置为true,表示不加载Bean的元数据信息,同时打印Banner图

容器创建#

我们通过createApplicationContext来创建容器,首先根据服务类型创建ConfigurableApplicationContext,默认的服务类型是servlet,所以创建的AnnotationConfigServletWebServerApplicationContext,在这个过程中会构造诸如:

  1. 存放和生产我们Bean实例的Bean工厂DefaultListableBeanFactory
  2. 用来解析@Component、@ComponentScan等注解的配置类后处理器ConfigurationClassPostProcessor
  3. 用来解析@Autowired、@Value、@Inject等注解的Bean后处理器AutowiredAnnotationBeanPostProcessor等在内的属性对象。

接着通过prepareContext方法对容器中的部分属性进行初始化

Copy
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); // 第一步 postProcessApplicationContext(context); // 第二步 applyInitializers(context); // 第三步 ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } // Load the sources Set<Object> sources = getAllSources(); // 第四步 load(context, sources.toArray(new Object[0])); listeners.contextLoaded(context); }
  1. 先用postProcessApplicationContext 方法设置Bean名称生成器、资源加载器、类型转换器。
  2. 接着执行之前我们加载进来的上下文初始化 ApplicationContextInitializer
  3. 为容器注册启动参数、Banner、Bean引用策略和懒加载等。
  4. 通过Bean定义加载器将启动类在内的资源加载到Bean定义池BeanDefinitionMap中,以便后续根据Bean定义创建Bean对象。
  5. 发布资源加载完成事件。

填充容器#

Copy
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(); } } }
  1. 通过prepareRefresh方法,在已有的系统环境基础上准备servlet相关的环境Environment,通过initServletPropertySources对servlet初始化参数servletContextInitParamsservletConfigInitParams进行赋值。然后通过validateRequiredProperties方法检验是否有必填的环境变量。

  2. 通过obtainFreshBeanFactory,通过obtainFreshBeanFactory重新构造BeanFactory。

  3. prepareBeanFactory方法主要在BeanFactory中准备 BeanClassLoader(类加载器)BeanExpressionResolver(表达式解析器)PropertyEditorRegistrar(配置文件处理器)等系统级处理器,用以解析Aware接口的ApplicationContextAwareProcessor,用来处理自定义监听器注册和注销的ApplicationListenerDetector。同时会注册一些特殊的Bean和系统级Bean,比如容器本身BeanFactoryApplicationContext,系统环境environment,系统属性systemProperties将它们放入特殊对象池和单例池中。

  4. 通过postProcessBeanFactory对BeanFactory进行额外设置或修改,这里主要定义了包括requestsession在内的servlet相关作用域Scopes,同时也注册跟Servlet相关的一些特殊Bean。包括ServletRequest,ServletResponse,HttpSession,WebRequest

    Copy
    public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory, @Nullable ServletContext sc) { beanFactory.registerScope("request", new RequestScope()); beanFactory.registerScope("session", new SessionScope()); if (sc != null) { ServletContextScope appScope = new ServletContextScope(sc); beanFactory.registerScope("application", appScope); sc.setAttribute(ServletContextScope.class.getName(), appScope); } beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory()); beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory()); beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory()); beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory()); }
  5. 执行invokeBeanFactoryPostProcessors,会逐一执行在第三个大阶段容器创建中注册的各种BeanFactory后置处理器beanFactoryPostProcessor。其中最主要的就是用来加载所有Bean定义的ConfigurationClassPostProcessor,通过它加载所有的@Configuration配置类。同时检索指定的Bean扫描路径compoentScans,然后通过Bean扫描器ClassPathBeanDefinitionScannerdoScan扫描每个类,将所有扫描的Bean定义,都放到Bean定义池beanDefinitionMap中。同样也会扫描所有加了@Bean、@Import等注解的类和方法,将它们对应的Bean定义也都放到Bean定义池中。

  6. 通过registerBeanPostProcessors检索所有的Bean后置处理器,同时根据指定的order进行排序,然后放入后置处理器池beanPostProcessors中,没一个后置处理器都会在Bean初始化之前和之后分别执行对应的逻辑。

    Copy
    public static void registerBeanPostProcessors( ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) { String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false); for (String ppName : postProcessorNames) { postProcessors.add(beanFactory.getBean(ppName,BeanPostProcessor.class)) } sortPostProcessors(postprocessors, beanFactory); registerBeanPostProcessors(beanFactory, postprocessors); // ... }
  7. initMessageSource从单例池中获名为messageSource的Bean放入ApplicationContext中用于实现国际化。

  8. initApplicationEventMulticaster 从单例池中获取名为applicationEventMulticaster的Bean放入ApplicationContext中用于自定义广播事件,有了它就可以通过publishEvent方法进行事件的发布。

  9. 通过onRefresh构造并启动Web服务器,先查找实现了ServletWebServerFactory这个接口的应用服务器Bean,默认是tomcat,接下来通过getWebServer方法构造一个Tomcat对象,同时通过start方法启动。这样容器内的web服务器就开始运行了

  10. registerListeners,在bean中查找所有的监听器Bean,将它们注册到第8步构造的消息广播器applicationEventMulticaster

  11. finishBeanFactoryInitialization这一步来生产我们所有的Bean整体分为:构造对象、填充属性、初始化实例、注册销毁 四个步骤

  12. finishRefresh构造并注册lifecycleProcessor(生命周期管理器),同时会调用所有实现了LifeCycle的Bean中的start方法。发布一个容器刷新完成的事件。

执行Runner#

Spring Boot提供了ApplicationRunnerCommandLineRunner两种类型的接口,可以让我们在容器启动完成后,来回调执行run方法。

Copy
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); } } }
posted @   loveletters  阅读(149)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示
目录