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会调用很多监听器对外进行扩展