乐趣区

关于springboot:聊聊springboot的启动事件

本文次要钻研一下 springboot 的启动事件

SpringApplicationEvent

org/springframework/boot/context/event/SpringApplicationEvent.java

public abstract class SpringApplicationEvent extends ApplicationEvent {private final String[] args;

    public SpringApplicationEvent(SpringApplication application, String[] args) {super(application);
        this.args = args;
    }

    public SpringApplication getSpringApplication() {return (SpringApplication) getSource();}

    public final String[] getArgs() {return this.args;}

}

SpringApplicationEvent 继承了 ApplicationEvent,它有几个子类分表是 ApplicationStartingEvent、ApplicationEnvironmentPreparedEvent、ApplicationContextInitializedEvent、ApplicationPreparedEvent、ApplicationStartedEvent、ApplicationReadyEvent,期间有异样则抛出 ApplicationFailedEvent

SpringApplication.run

org/springframework/boot/SpringApplication.java

    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);
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
            configureIgnoreBeanInfo(environment);
            Banner printedBanner = printBanner(environment);
            context = createApplicationContext();
            exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class}, context);
            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);
            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;
    }

SpringApplication 的 run 办法,先触发 listeners.starting(),而后执行了 prepareEnvironment,之后 createApplicationContext,再进行 prepareContext 和 refreshContext,最初触发 listeners.started(context),之后执行 callRunners,最初触发 listeners.running(context),如有异样则会执行 handleRunFailure

prepareEnvironment

    private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
        // Create and configure the environment
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        ConfigurationPropertySources.attach(environment);
        listeners.environmentPrepared(environment);
        bindToSpringApplication(environment);
        if (!this.isCustomEnvironment) {environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                    deduceEnvironmentClass());
        }
        ConfigurationPropertySources.attach(environment);
        return environment;
    }

prepareEnvironment 会触发 listeners.environmentPrepared(environment),即公布 ApplicationEnvironmentPreparedEvent

prepareContext

    private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
            SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {context.setEnvironment(environment);
        postProcessApplicationContext(context);
        applyInitializers(context);
        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) {((DefaultListableBeanFactory) beanFactory)
                    .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        if (this.lazyInitialization) {context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }
        // Load the sources
        Set<Object> sources = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        load(context, sources.toArray(new Object[0]));
        listeners.contextLoaded(context);
    }

prepareContext 会触发 listeners.contextPrepared(context),即公布 ApplicationContextInitializedEvent,执行实现之后触发 listeners.contextLoaded(context),即公布 ApplicationPreparedEvent

refreshContext

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

    protected void refresh(ApplicationContext applicationContext) {Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
        ((AbstractApplicationContext) applicationContext).refresh();}    

refreshContext 会执行 refresh 办法

org/springframework/context/support/AbstractApplicationContext.java

    public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {
            // 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);

                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);

                // 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();}
        }
    }

refresh 先执行 postProcessBeanFactory,比方增加 BeanPostProcessors,之后执行 invokeBeanFactoryPostProcessors,即执行 BeanFactoryPostProcessor 的 postProcessBeanFactory 办法
refresh 执行完之后触发 listeners.started(context) 即公布 ApplicationStartedEvent
执行完 callRunners 之后触发 listeners.running(context)即公布 ApplicationReadyEvent

handleRunFailure

    private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception,
            Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) {
        try {
            try {handleExitCode(context, exception);
                if (listeners != null) {listeners.failed(context, exception);
                }
            }
            finally {reportFailure(exceptionReporters, exception);
                if (context != null) {context.close();
                }
            }
        }
        catch (Exception ex) {logger.warn("Unable to close ApplicationContext", ex);
        }
        ReflectionUtils.rethrowRuntimeException(exception);
    }

触发 listeners.failed(context, exception)即公布 ApplicationFailedEvent

小结

SpringApplication 的 run 办法,先触发 listeners.starting()(ApplicationStartingEvent),而后执行了 prepareEnvironment(ApplicationEnvironmentPreparedEvent),之后 createApplicationContext,再进行 prepareContext 和 refreshContext(ApplicationContextInitializedEvent–>ApplicationPreparedEvent),最初触发 listeners.started(context)(ApplicationStartedEvent),之后执行 callRunners,最初触发 listeners.running(context)(ApplicationReadyEvent),期间有异样会执行 handleRunFailure,触发 listeners.failed(context, exception)(ApplicationFailedEvent)

其中 refresh 的时候会执行 BeanFactoryPostProcessor 的 postProcessBeanFactory 办法
整体程序如下:ApplicationStartingEvent –> ApplicationEnvironmentPreparedEvent –> ApplicationContextInitializedEvent –> ApplicationPreparedEvent –> ApplicationStartedEvent –> ApplicationReadyEvent,期间有异样则抛出 ApplicationFailedEvent

退出移动版