SpringBoot源码系列(二):深入理解启动原理

649 阅读2分钟

在SpringBoot源码系列(一):深入理解自动配置原理笔者已经带大家了解了自动配置涉及到的基本注解以及流程,但是SpringBoot在什么时候去自动配置的呢,这得从Spring Boot启动流程开始着手分析。

SpringApplication.run()

SpringBoot项目一般都是有个主启动类,并且主启动类里面会有个main方法,我们知道main方法是java程序启动的入口,因此研究Spring Boot的启动原理,我们可以从这个main方法为切入点。

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        // DemoApplication.class 即为我们的启动类
        SpringApplication.run(DemoApplication.class, args);
    }

}

SpringApplication.run方法最重调用的是org.springframework.boot.SpringApplication#run(java.lang.String...)方法,这个方法主要是创建上下文,返回类型为ConfigurableApplicationContext的上下文。 其流程为:

  1. 启动监视器
  2. 获取并开启事件监听器
  3. 准备环境:加载各类配置文件,比如properties
  4. 创建上下文
  5. 创建对象工厂
  6. 刷新上下文
  7. 暂停监视器
public ConfigurableApplicationContext run(String... args) {
        // 1. 启动监视器
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
        this.configureHeadlessProperty();
        // 2. 获取并开启监听器
        SpringApplicationRunListeners listeners = this.getRunListeners(args);
        listeners.starting();

        Collection exceptionReporters;
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            // 3. 准备环境
           ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            this.configureIgnoreBeanInfo(environment);
            Banner printedBanner = this.printBanner(environment);
            // 4. 创建上下文
            context = this.createApplicationContext();
            // 5. 创建对象工厂
            exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
            this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            // 6. 刷新上下文
            this.refreshContext(context);
            this.afterRefresh(context, applicationArguments);
           // 7. 暂停监视器
            stopWatch.stop();
            if (this.logStartupInfo) {
                (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
            }

            listeners.started(context);
            this.callRunners(context, applicationArguments);
        } catch (Throwable var10) {
            this.handleRunFailure(context, var10, exceptionReporters, listeners);
            throw new IllegalStateException(var10);
        }

        try {
            listeners.running(context);
            return context;
        } catch (Throwable var9) {
            this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
            throw new IllegalStateException(var9);
        }
    }

refresh

在上面的第6步中,是应用上下文的刷新,这一步是调用的org.springframework.context.support.AbstractApplicationContext#refresh()方法,也是Spring的主要启动流程。this.onRefresh() 是个抽象方法,具体的上下文刷新交由子类来实现。

在实现类中,我们可以找到ServeltWebServerApplicationContext是其中一个实现。

public void refresh() throws BeansException, IllegalStateException {
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }

                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }

        }
    }

ServeltWebServerApplicationContext

ServeltWebServerApplicationContext 的onRefresh方法会调用createWebServer方法创建一个Tomcat实例,并初始化Servlet上下文。

    protected void onRefresh() {
        super.onRefresh();

        try {
            this.createWebServer();
        } catch (Throwable var2) {
            throw new ApplicationContextException("Unable to start web server", var2);
        }
    }

    protected void doClose() {
        if (this.isActive()) {
            AvailabilityChangeEvent.publish(this, ReadinessState.REFUSING_TRAFFIC);
        }

        super.doClose();
    }

    private void createWebServer() {
        WebServer webServer = this.webServer;
        ServletContext servletContext = this.getServletContext();
        if (webServer == null && servletContext == null) {
            ServletWebServerFactory factory = this.getWebServerFactory();
            this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
            this.getBeanFactory().registerSingleton("webServerGracefulShutdown", new WebServerGracefulShutdownLifecycle(this.webServer));
            this.getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer));
        } else if (servletContext != null) {
            try {
                this.getSelfInitializer().onStartup(servletContext);
            } catch (ServletException var4) {
                throw new ApplicationContextException("Cannot initialize servlet context", var4);
            }
        }

        this.initPropertySources();
    }

总结

Spring Boot内置Tomcat启动原理,我们应该有了大致的了解,如果面试官问你,Spring Boot Tomcat的启动原理,你可以这样回答:

  1. Spring Boot 的主启动类中main方法调用了SpringApplication.run()方法,这个run方法是启动的入口,主要进行了监听器的注册,环境的准备,创建上下文,刷新上下文,并返回一个类型为ConfigurableApplicationContext的上下文。
  2. 其中刷新上下文会调用ServletWebServerApplicationContext的onRefresh()方法,会通过工厂创建Tomcat对象,并初始化Servlet上下文。