springboot专题二:springboot的启动流程

一、从熟知的启动类开始

下面这个启动类是我们熟悉的springboot的启动类:代码是自己写的一个简单的springboot的demo: https://gitee.com/leijisong/springcloud-demo

@SpringBootApplication(scanBasePackages = {"com.mall"})
@EntityScan(basePackages = {"com.mall.domain.dataobject"})
@EnableJpaRepositories(basePackages = "com.mall.domain")
@EnableDiscoveryClient
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
@EnableRabbit
public class MallApplication {

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

}

1. 调用SpringApplication.run(MallApplication.class, args); 启动了我们的SpringBoot应用:

2. 查看run方法:使用自定义SpringApplication进行启动

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

2.1 创建SpringApplication  -->  new SpringApplication(primarySources)

源码如下:

    /**
     * Create a new {@link SpringApplication} instance. The application context will load
     * beans from the specified primary sources (see {@link SpringApplication class-level}
     * documentation for details. The instance can be customized before calling
     * {@link #run(String...)}.
     * @param resourceLoader the resource loader to use
     * @param primarySources the primary bean sources
     * @see #run(Class, String[])
     * @see #setSources(Set)
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
     // 将启动类放入到primarySources
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 根据classpath下的类,感知当前web应用类型,如WebApplicationType.SERVLET,WebApplicationType.REACTIVE
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 从spring.factories中获取所有的key为:org.springframework.context.ApplicationContextInitializer   setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.
class));
     // 从spring.factories中获取所有的key为:org.springframework.context.ApplicationListener setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.
class));
// 根据main方法得到mainApplicationClass
this.mainApplicationClass = deduceMainApplicationClass(); }

我们看下spring.factories这个文件(这个文件通过第一个专题应该很熟悉了)。看看ApplicationContextInitializer和ApplicationListener都定义了哪些?

实际上,远不止这些,通过扩展的还有:

ApplicationContextInitializer:

ApplicationListener:

 

 总结:这里主要是做了一些初始化动作

  • 获取启动类加载ioc容器
  • 获取web应用类型
  • 通过spring.factories读取对外扩展的ApplicationContextInitializer ,ApplicationListener
  • 根据main推算出所在类

2.2 启动

 启动的核心逻辑源码如下:

    /**
     * Run the Spring application, creating and refreshing a new
     * {@link ApplicationContext}.
     * @param args the application arguments (usually passed from a Java main method)
     * @return a running {@link ApplicationContext}
     */
    public ConfigurableApplicationContext run(String... args) {
     // StopWatch主要用来记录Springboot的启动耗时 StopWatch stopWatch
= new StopWatch();
// 启动的开始时间 stopWatch.start();
// Spring上下文接口,继承ApplicationContext ConfigurableApplicationContext context
= null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 开启了headless模式 configureHeadlessProperty();
// 从spring.factories读取SpringApplicationRunListener组件,发布事件/运行监听 SpringApplicationRunListeners listeners
= getRunListeners(args);
// 发布事件:ApplicationStartingEvent listeners.starting();
try {
// 根据命令行参数,实例化ApplicationArguments ApplicationArguments applicationArguments
= new DefaultApplicationArguments( args);
// 基于监听器与初始化环境:包括环境变量和配置文件的信息 ConfigurableEnvironment environment
= prepareEnvironment(listeners, applicationArguments);
// 忽略beaninfo的Bean configureIgnoreBeanInfo(environment);
// 打印Bananer横幅 Banner printedBanner
= printBanner(environment);
// 根据webApplicationType创建上下文 context
= createApplicationContext(); exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context);
       // 初始化Spring上下文 prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 关键:加载IOC容器;加载自动配置类 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; }
 2.2.1 prepareEnvironment(listeners,applicationArguments);

这个主要做的是基于监听器的环境初始化

源码:

private ConfigurableEnvironment prepareEnvironment(
            SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
        // 根据webApplicationType创建环境,创建时就会读取java环境变量和系统变量
        ConfigurableEnvironment environment = getOrCreateEnvironment();
// 将命令行参数读取环境变量中 configureEnvironment(environment, applicationArguments.getSourceArgs()); listeners.environmentPrepared(environment);
     // 将spring.main开头的信息都绑定SpringApplication bindToSpringApplication(environment);
if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()) .convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); }
// 更新PropertySource ConfigurationPropertySources.attach(environment);
return environment; }
private ConfigurableEnvironment getOrCreateEnvironment() {
        if (this.environment != null) {
            return this.environment;
        }
        switch (this.webApplicationType) {
        case SERVLET:
            return new StandardServletEnvironment();
        case REACTIVE:
            return new StandardReactiveWebEnvironment();
        default:
            return new StandardEnvironment();
        }
    }
protected void configureEnvironment(ConfigurableEnvironment environment,
            String[] args) {
        if (this.addConversionService) {
            ConversionService conversionService = ApplicationConversionService
                    .getSharedInstance();
            environment.setConversionService(
                    (ConfigurableConversionService) conversionService);
        }
        configurePropertySources(environment, args);
        configureProfiles(environment, args);
    }
protected void bindToSpringApplication(ConfigurableEnvironment environment) {
        try {
            Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
        }
        catch (Exception ex) {
            throw new IllegalStateException("Cannot bind to SpringApplication", ex);
        }
    }
2.2.2 prepareContext

源码:

private void prepareContext(ConfigurableApplicationContext context,
            ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
// 上下文后置处理 postProcessApplicationContext(context);
// 将之前拿到的所有ApplicationContextInitializer,遍历执行initialize方法 applyInitializers(context);
// 发布ApplicationContextInitializedEvent listeners.contextPrepared(context);
if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // 获取Spring上下文的Bean工厂 ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); }
// 如果出现两个同名bean。直接抛异常
if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } // 加载资源 Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty");
// 读取主启动类,注册成Bean load(context, sources.toArray(
new Object[0]));
// 读取完配置类之后,发送ApplicationPreparedEvent listeners.contextLoaded(context); }
    /**
     * Apply any {@link ApplicationContextInitializer}s to the context before it is
     * refreshed.
     * @param context the configured ApplicationContext (not refreshed yet)
     * @see ConfigurableApplicationContext#refresh()
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    protected void applyInitializers(ConfigurableApplicationContext context) {
        for (ApplicationContextInitializer initializer : getInitializers()) {
            Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
                    initializer.getClass(), ApplicationContextInitializer.class);
            Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
            initializer.initialize(context);
        }
    }
public void contextPrepared(ConfigurableApplicationContext context) {
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.contextPrepared(context);
        }
    }
    /**
     * Return an immutable set of all the sources that will be added to an
     * ApplicationContext when {@link #run(String...)} is called. This method combines any
     * primary sources specified in the constructor with any additional ones that have
     * been {@link #setSources(Set) explicitly set}.
     * @return an immutable set of all sources
     */
    public Set<Object> getAllSources() {
        Set<Object> allSources = new LinkedHashSet<>();
        if (!CollectionUtils.isEmpty(this.primarySources)) {
            allSources.addAll(this.primarySources);
        }
        if (!CollectionUtils.isEmpty(this.sources)) {
            allSources.addAll(this.sources);
        }
        return Collections.unmodifiableSet(allSources);
    }
public void started(ConfigurableApplicationContext context) {
     for (SpringApplicationRunListener listener : this.listeners) {
         listener.started(context);
     }
}

@Override
public void started(ConfigurableApplicationContext context) {
context.publishEvent(
new ApplicationStartedEvent(this.application, this.args, context));
}
 

2.2.3 refreshContext

源码:

private void refreshContext(ConfigurableApplicationContext context) {
        refresh(context);
        if (this.registerShutdownHook) {
            try {
                context.registerShutdownHook();
            }
            catch (AccessControlException ex) {
                // Not allowed in some environments.
            }
        }
    }
    @Override
    public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.            
            // 设置Spring的启动事件,开启活跃状态;初始化属性源信息并验证必要属性
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.            
            // 从spring容器获取BeanFactory并进行相关设置为后续使用做准备
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.                
                // 继上一步beanfactory设置之后进行后续操作,不同spring容器进行不同操作
                postProcessBeanFactory(beanFactory);

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

                // Register bean processors that intercept bean creation.                
                // 使用了PostProcessorRegistrationDelegate类的registerBeanPostProcessors方法执行          
                // 1)先找出实现了PriorityOrdered接口的BeanPostProcessor并排序后加到BeanFactory的BeanPostProcessor集合中                          
                // 2)找出实现了Ordered接口的BeanPostProcessor并排序后加到BeanFactory的BeanPostProcessor集合中                          
                // 3)没有实现PriorityOrdered和Ordered接口的BeanPostProcessor加到BeanFactory的BeanPostProcessor集合中
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.                
                // 初始化国际化属性
                initMessageSource();

                // Initialize event multicaster for this context.                
                // 初始化事件广播器,用于发布事件。 EventPublishingRunlistener会监听事件,在run函数之前contextPrepared时候已经注入了。                 
                // 这个时候不需要注册,只要拿到BeanFactory的广播器直接设置到spring容器,如果没有再自己初始化
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.                
                // 不同容器各自实现,比如ConfigEmbeddedWebApplicationContext中会调用
          // createEmbeddedServletContainer方法去创建内置的Servlet容器, // 目前支持 tomcat,jetty,undertow。
onRefresh(); // Check for listener beans and register them. // 把spring容器内的listener和beanfactory的listener都添加到广播器中 registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. // 实例化BeanFactory 中已经被注册但是没被实例化的所有实例,懒加载除外。 // 比如invokeBeanFactoryPostProcessors方法中根据各种注解解析出来的类,都会初始化。
// 初始化的过程中各种BeanPostProcessor开始起作用
finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. // 初始化生命周期处理器LifecycleProcessor并调用其onrefresh方法,           // 找到SmartLifecycle接口的所有实现类并调用start方法,发布事件告知listener, // 如果设置了JMX相关属性,还会调用LiveBeansView的registerApplicationContext方法 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(); } } }

 

总结:

  • 初始化SpringApplication 从spring.factories 读取 listener ApplicationContextInitializer 。
  • 运行run方法
  • 读取 环境变量 配置信息
  • 创建springApplication上下文:ServletWebServerApplicationContext
  • 预初始化上下文 : 读取启动类
  • 调用refresh 加载ioc容器
  • 加载所有的自动配置类
  • 创建servlet容器
  • 在这个过程中springboot会调用很多监听器对外进行扩展
posted @ 2021-06-14 18:17  未知的九月  阅读(857)  评论(0编辑  收藏  举报