一、從熟知的啟動類開始
下面這個啟動類是我們熟悉的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會調用很多監聽器對外進行擴展