美文网首页
SpringBoot 启动过程

SpringBoot 启动过程

作者: 云中人山 | 来源:发表于2020-09-14 10:51 被阅读0次

    本文基于SpringBoot 2.1.12.RELEASE

    一个常见的SpringBoot启动类为

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

    其中 @SpringBootApplication@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan三者的聚合

    其中@SpringBootConfiguration@Configuration无异, ComponentScan也不用多说,@EnableAutoConfiguation则实际上用了@Import({AutoConfigurationImportSelector.class}),用于将指定目录下的配置类进行加载,其原理类似SPI,也无须多讲。

    1. SpringApplication#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);
        }
    

    可以看到,run()方法最后返回的是 new SpringApplication(primarySources).run(args)

    2. SpringApplication 的构造函数

    接下来,进入此处的SpringApplication的构造函数,到最底层调用的是

    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
            this.resourceLoader = resourceLoader;
            Assert.notNull(primarySources, "PrimarySources must not be null");
            // 1.将配置类放入
            this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
            // 2.判断webApplication的类型,枚举有  NONE  SERVLET REACTIVE
            this.webApplicationType = WebApplicationType.deduceFromClasspath();
            
            // 3.加载ApplicationContextInitializer配置并实例化,关联到SpringApplication#initializer 
          //底层是SpringFactoriesLoader.loadFactoryNames(type, classLoader)
            setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
            // 4.与上一句类似 加载ApplicationListener配置并实例化,关联到SpringApplication#listener
            setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
            // 5.推断应用引导类
            this.mainApplicationClass = deduceMainApplicationClass();
    }
    
    

    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));中具体的调用路径为

    // 加载制定目录下对应类型的类,并初始化后返回实例
    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
            ClassLoader classLoader = getClassLoader();
            // 作用是获取META-INF/spring.factories下的所有ApplicationContextInitializer类
            Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
            // 初始化对应的类
            List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
            // 排序
            AnnotationAwareOrderComparator.sort(instances);
            return instances;
    }
    
    // 将所有的ApplicationContextInitializer 关联到SpringApplication#initializers中
    public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
            this.initializers = new ArrayList<>();
            this.initializers.addAll(initializers);
    }
    

    this.mainApplicationClass = deduceMainApplicationClass();的具体函数为

    // 推断引导类
    private Class<?> deduceMainApplicationClass() {
            try {
                // 获取当前线程执行栈
                StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
                for (StackTraceElement stackTraceElement : stackTrace) {
                    // 哪个类包含main方法
                    if ("main".equals(stackTraceElement.getMethodName())) {
                        return Class.forName(stackTraceElement.getClassName());
                    }
                }
            }
            catch (ClassNotFoundException ex) {
                // Swallow and continue
            }
            return null;
        }
    

    3. SpringApplication(primarySources).run方法—— 真正启动

    // 创建并refresh一个ApplicationContext
    public ConfigurableApplicationContext run(String... args) {
            StopWatch stopWatch = new StopWatch();
            stopWatch.start();
            ConfigurableApplicationContext context = null;
            Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
            //设置系统属性『java.awt.headless』,为true则启用headless模式支持
            configureHeadlessProperty();
            //通过*SpringFactoriesLoader*检索*META-INF/spring.factories*,
           //找到声明的所有SpringApplicationRunListener的实现类并将其实例化,
           //之后逐个调用其started()方法,广播SpringBoot要开始执行了
            SpringApplicationRunListeners listeners = getRunListeners(args);
            //发布应用开始启动事件
            listeners.starting();
            try {
            //初始化参数
                ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
                //创建并配置当前SpringBoot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile),
            //并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法,广播Environment准备完毕。
                ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
                configureIgnoreBeanInfo(environment);
                //打印banner
                Banner printedBanner = printBanner(environment);
                //创建应用上下文
                context = createApplicationContext();
                //通过*SpringFactoriesLoader*检索*META-INF/spring.factories*,获取并实例化异常分析器
                exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                        new Class[] { ConfigurableApplicationContext.class }, context);
                //为ApplicationContext加载environment,之后逐个执行ApplicationContextInitializer的initialize()方法来进一步封装ApplicationContext,
            //并调用所有的SpringApplicationRunListener的contextPrepared()方法,【EventPublishingRunListener只提供了一个空的contextPrepared()方法】,
            //之后初始化IoC容器,并调用SpringApplicationRunListener的contextLoaded()方法,广播ApplicationContext的IoC加载完成,
            //这里就包括通过**@EnableAutoConfiguration**导入的各种自动配置类。
                prepareContext(context, environment, listeners, applicationArguments, printedBanner);
                //刷新上下文
                refreshContext(context);
                //再一次刷新上下文,其实是空方法,可能是为了后续扩展。
                afterRefresh(context, applicationArguments);
                stopWatch.stop();
                if (this.logStartupInfo) {
                    new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
                }
                //发布应用已经启动的事件
                listeners.started(context);
                //遍历所有注册的ApplicationRunner和CommandLineRunner,并执行其run()方法。
            //我们可以实现自己的ApplicationRunner或者CommandLineRunner,来对SpringBoot的启动过程进行扩展。
                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;
        }
    
    }
    

    其中,SpringApplicationRunListeners listeners = getRunListeners(args);getRunListeners中调用了getSpringFactorieInstances方法,其作用就是加载制定目录下的配置类

    
    private SpringApplicationRunListeners getRunListeners(String[] args) {
            Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
        // 获取配置的SpringApplicationRunListener
            return new SpringApplicationRunListeners(logger,
                    getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
    }
    
    3.1 创建上下文

    context = createApplicationContext()创建上下文的语句,其源码为

    /**
        默认上下文
         */
        public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
                + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
    
        /**
        reactive上下文
         */
        public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
                + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
    
    protected ConfigurableApplicationContext createApplicationContext() {
            Class<?> contextClass = this.applicationContextClass;
            if (contextClass == null) {
                try {
                    switch (this.webApplicationType) {
                    case SERVLET:
                        contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                        break;
                    case REACTIVE:
                        contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                        break;
                    default:
                        contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
                    }
                }
                catch (ClassNotFoundException ex) {
                    throw new IllegalStateException(
                            "Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass",
                            ex);
                }
            }
            return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
        }
    

    即在默认web环境下,上下文的实现为AnnotationConfigServletWebServerApplicationContext

    3.2 上下文运行前准备

    prepareContext(context, environment, listeners, applicationArguments, printedBanner);的源码为

    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
                SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
            // 设置环境 必须在refresh之前 原因为若使用 AbstractApplicationContext#createEnvironment,则由BeanFactoryPostProcessor实现environment属性的装载,但是无法保证在PostProcessor中最早执行
            context.setEnvironment(environment);
            // 上下文后置处理  处理resourceLoader,classLoader以及conversionService
            postProcessApplicationContext(context);
            // 迭代执行所有的ApplicationContextInitializer的实现类
            applyInitializers(context);
            // 发布 contextPrepared的事件
            listeners.contextPrepared(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 DefaultListableBeanFactory) {
                //允许Override
                ((DefaultListableBeanFactory) beanFactory)
                        .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
            }
            // 读取所有的源信息 参照第一步的 primarySources
            Set<Object> sources = getAllSources();
            Assert.notEmpty(sources, "Sources must not be empty");
            // 将所有的上下文配置源(sources)读取并放入上下文中
            load(context, sources.toArray(new Object[0]));
            // 执行SpringApplicationRunListener#contextLoaded回调
            // 发布ApplicationLoadedEvent
            listeners.contextLoaded(context);
        }
    

    可以看到,在此期间内,listeners发送了两次事件,一次是contextPrepared,一次是contextLoaded

    3.3 refreshContext

    内部为

    private void refreshContext(ConfigurableApplicationContext context) {
            refresh(context);
            if (this.registerShutdownHook) {
                try {
                    context.registerShutdownHook();
                }
                catch (AccessControlException ex) {
                    // Not allowed in some environments.
                }
            }
        }
    

    其实就是调用的((AbstractApplicationContext) applicationContext).refresh()

    至此,我们基本上把SpringBoot的启动的大致过程摸清楚了。

    在查询资料的过程中,发现一篇文章写的不错,是侧重于启动过程中Tomcat的处理,有兴趣可以看看

    Tomcat在SpringBoot中是如何启动的

    相关文章

      网友评论

          本文标题:SpringBoot 启动过程

          本文链接:https://www.haomeiwen.com/subject/xbfoektx.html