天空闲话 发表于 2023-6-15 10:02:29

springboot启动流程 (1) 流程概览

本文将通过阅读源码方式分析SpringBoot应用的启动流程,不涉及Spring启动部分(有相应的文章介绍)。
本文不会对各个流程做展开分析,后续会有文章介绍详细流程。
SpringApplication类

应用启动入口

使用以下方式启动一个SpringBoot应用:
@SpringBootApplication
public class SpringBootDemoApplication {

public static void main(String[] args) {
    SpringApplication.run(SpringBootDemoApplication.class, args);
}
}run方法

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
        return run(new Class<?>[] { primarySource }, args);
}

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return new SpringApplication(primarySources).run(args);
}

public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
        configureHeadlessProperty();
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();
        try {
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
                // 获取应用env
                ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
                configureIgnoreBeanInfo(environment);
                // 打印banner
                Banner printedBanner = printBanner(environment);
                // 创建ApplicationContext
                context = createApplicationContext();
                exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                                new Class[] { ConfigurableApplicationContext.class }, context);
                // 一些准备工作
                prepareContext(context, environment, listeners, applicationArguments, printedBanner);
                // refresh ApplicationContext
                refreshContext(context);
                afterRefresh(context, applicationArguments);
                stopWatch.stop();
                if (this.logStartupInfo) {
                        new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
                }
                // 调用listener
                listeners.started(context);
                // 调用ApplicationRunner和CommandLineRunner
                callRunners(context, applicationArguments);
        } catch (Throwable ex) {
                handleRunFailure(context, ex, exceptionReporters, listeners);
                throw new IllegalStateException(ex);
        }

        try {
                listeners.running(context);
        } catch (Throwable ex) {
                handleRunFailure(context, ex, exceptionReporters, null);
                throw new IllegalStateException(ex);
        }
        return context;
}获取应用env

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
                ApplicationArguments applicationArguments) {
        // 创建StandardServletEnvironment, 会初始化四个PropertySource:
        // servletConfigInitParams, servletContextInitParams, systemProperties, systemEnvironment
        // 比如-Dserver.port=8888会在systemProperties中
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        // 添加defaultProperties和命令行配置参数即CommandLinePropertySource
        // 通常都没有这两个配置
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        // 使用ConfigurationPropertySourcesPropertySource封装并暴露所有的PropertySource集
        ConfigurationPropertySources.attach(environment);
        // 添加ApplicationEnvironmentPreparedEvent事件并触发multicastEvent加载应用配置文件
        listeners.environmentPrepared(environment);
        // 将spring.main.xx配置加载到SpringApplication对象
        bindToSpringApplication(environment);
        if (!this.isCustomEnvironment) {
                environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                                deduceEnvironmentClass());
        }
        ConfigurationPropertySources.attach(environment);
        return environment;
}加载配置文件的入口在ConfigFileApplicationListener类中:
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        addPropertySources(environment, application.getResourceLoader());
}

protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
        // 添加RandomValuePropertySource
        RandomValuePropertySource.addToEnvironment(environment);
        // 加载配置文件
        new Loader(environment, resourceLoader).load();
}加载配置文件的源码较多,此处不做记录,简单梳理一下流程:

[*]加载active profile配置文件

[*]如果配置了spring.config.additional-location或spring.config.location参数,会使用它们作为配置文件。如果这两个参数值是目录,则会从这两个目录下查找配置文件
[*]默认从classpath:/,classpath:/config/,file:./,file:./config/*/,file:./config/目录下查找application-xx.properties或application-xx.yml文件
[*]使用PropertiesPropertySourceLoader和YamlPropertySourceLoader解析配置文件
[*]会将配置参数封装成OriginTrackedMapPropertySource类型对象,使用applicationConfig: 之类的字符串作为PropertySource的名称

[*]加载默认的application.properties或application.yml文件
[*]解析出来的所有PropertySource都会添加到environment的propertySources中,propertySources是一个MutablePropertySources对象,管理着所有的PropertySource集,在这个过程中,添加的先后顺序决定了配置的优先级
创建ApplicationContext

protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
                try {
                        switch (this.webApplicationType) {
                        case SERVLET:
                                // AnnotationConfigServletWebServerApplicationContext类
                                contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                                break;
                        case REACTIVE:
                                // AnnotationConfigReactiveWebServerApplicationContext类
                                contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                                break;
                        default:
                                // AnnotationConfigApplicationContext类
                                contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
                        }
                } catch (ClassNotFoundException ex) {
                        throw new IllegalStateException("Unable create a default ApplicationContext", ex);
                }
        }
        return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}https://img2023.cnblogs.com/blog/1258602/202306/1258602-20230615090245308-2055938332.jpg
https://img2023.cnblogs.com/blog/1258602/202306/1258602-20230615090254590-910448328.jpg
prepareContext

private void prepareContext(
                ConfigurableApplicationContext context, ConfigurableEnvironment environment,
                SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments,
                Banner printedBanner) {
        context.setEnvironment(environment);
        postProcessApplicationContext(context);
        // Apply any ApplicationContextInitializers to the context before it is refreshed.
        // ApplicationContextInitializers集是在创建SpringApplication对象的时候初始化的
        applyInitializers(context);
        // 触发contextPrepared事件
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
                logStartupInfo(context.getParent() == null);
                logStartupProfileInfo(context);
        }
        // 获取BeanFactory并注册必要的Bean
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 注册启动参数
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        // 注册banner printer
        if (printedBanner != null) {
                beanFactory.registerSingleton("springBootBanner", printedBanner);
        }
        // 设置是否允许Bean覆盖,使用spring.main.allowBeanDefinitionOverriding参数配置
        if (beanFactory instanceof DefaultListableBeanFactory) {
                ((DefaultListableBeanFactory) beanFactory)
                                .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        if (this.lazyInitialization) {
                context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }
        // Load the sources
        Set<Object> sources = getAllSources();
        // 将SpringApplication.run(Xxx.class, args)方法传入的Class注册到容器
        // 使用AnnotatedBeanDefinitionReader.register(Class<?>...)方法注册启动类
        load(context, sources.toArray(new Object));
        // 触发contextLoaded事件
        listeners.contextLoaded(context);
}refreshApplicationContext

protected void refresh(ConfigurableApplicationContext applicationContext) {
        applicationContext.refresh();
}调用的是ServletWebServerApplicationContext的refresh方法:
public final void refresh() throws BeansException, IllegalStateException {
        try {
                super.refresh();
        } catch (RuntimeException ex) {
                // 关闭web server
                WebServer webServer = this.webServer;
                if (webServer != null) {
                        webServer.stop();
                }
                throw ex;
        }
}绝大多数的refresh逻辑都在AbstractApplicationContext类里面,ServletWebServerApplicationContext中会在onRefresh阶段创建webServer:
protected void onRefresh() {
        super.onRefresh();
        try {
                createWebServer();
        } catch (Throwable ex) {
                throw new ApplicationContextException("Unable to start web server", ex);
        }
}调用ApplicationRunner和CommandLineRunner

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);
                }
        }
}SpringBootApplication注解

指示一个配置类,该类声明一个或多个@Bean方法,并触发自动配置和组件扫描。这是一个方便的注解,相当于声明@Configuration、@EnableAutoConfiguration和@ComponentScan注解。
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
                @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

        /**
       * Exclude specific auto-configuration classes such that they will never be applied.
       */
        @AliasFor(annotation = EnableAutoConfiguration.class)
        Class<?>[] exclude() default {};

        /**
       * Exclude specific auto-configuration class names such that they will never be
       * applied.
       */
        @AliasFor(annotation = EnableAutoConfiguration.class)
        String[] excludeName() default {};

        /**
       * Base packages to scan for annotated components. Use scanBasePackageClasses
       * for a type-safe alternative to String-based package names.
       */
        @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
        String[] scanBasePackages() default {};

        /**
       * Type-safe alternative to scanBasePackages for specifying the packages to
       * scan for annotated components. The package of each class specified will be scanned.
       */
        @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
        Class<?>[] scanBasePackageClasses() default {};

        /**
       * The BeanNameGenerator class to be used for naming detected components
       * within the Spring container.
       */
        @AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
        Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

        /**
       * Specify whether @Bean methods should get proxied in order to enforce
       * bean lifecycle behavior, e.g. to return shared singleton bean instances even in
       * case of direct @Bean method calls in user code. This feature requires
       * method interception, implemented through a runtime-generated CGLIB subclass which
       * comes with limitations such as the configuration class and its methods not being
       * allowed to declare final.
       */
        @AliasFor(annotation = Configuration.class)
        boolean proxyBeanMethods() default true;
}AutoConfigurationPackage注解

Registers packages with AutoConfigurationPackages. When no base packages or base package classes are specified, the package of the annotated class is registered.
@Configuration
public @interface SpringBootConfiguration {

        @AliasFor(annotation = Configuration.class)
        boolean proxyBeanMethods() default true;
}AutoConfigurationImportSelector类

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

SpringBoot会在onRefresh阶段创建webServer,首先从spring容器获取ServletWebServerFactory,然后调用getWebServer方法创建webServer。
getWebServer方法需要传入ServletContextInitializer集来初始化ServletContext。
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

        /**
       * Exclude specific auto-configuration classes such that they will never be applied.
       */
        Class<?>[] exclude() default {};

        /**
       * Exclude specific auto-configuration class names such that they will never be
       * applied.
       * 当类路径下没有指定的类时,可以使用这个属性指定排除的类
       */
        String[] excludeName() default {};
}我们开发者如果需要使用ServletContextInitializer来初始化ServletContext的话,也可以编写一个实现类,然后将其注册到spring容器即可。
另外,SpringBoot还会自动装配DispatcherServletAutoConfiguration类,这个类会创建DispatcherServlet和DispatcherServletRegistrationBean。DispatcherServlet是SpringWebMvc的最核心组件,DispatcherServletRegistrationBean实现了ServletContextInitializer接口,可以将DispatcherServlet注册到ServletContext。以TomcatServletWebServerFactory为例,这个类会通过TomcatStarter来调用所有的ServletContextInitializer,TomcatStarter实现了ServletContainerInitializer接口,Tomcat的ServletContext在启动阶段会调用ServletContainerInitializer的onStartup方法来初始化Servlet容器。
SpringBoot启动流程


[*]初始化environment应用配置参数:servletConfigInitParams, servletContextInitParams, systemProperties, systemEnvironment及配置文件等
[*]创建ApplicationContext对象,SpringBoot应用默认使用的是AnnotationConfigServletWebServerApplicationContext类
[*]prepareContext阶段:触发一些事件,将启动类注册到Spring容器
[*]refresh阶段:扫描应用组件,自动装配
[*]onRefresh阶段:创建并初始化WebServer
[*]调用ApplicationRunner和CommandLineRunner

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: springboot启动流程 (1) 流程概览