衆所周知,類上面帶有@SpringBootApplication註解的類,即爲springboot的啓動類。一個springboot項目只能有一個啓動類。咱們來分析一下SpringBoot項目的啓動過程,首先看看啓動類裏面都包含什麼java
@SpringBootApplication public class HelloWorldMainApplication { public static void main(String[] args) { //spring應用啓動起來 SpringApplication.run(HelloWorldMainApplication.class,args); } }
從上面的代碼中能夠看出真正起做用的是SpringApplication.run();這個方法,下面主要分析一下這個方法。
react
SpringApplication初始化時主要作三件事情:web
/** * 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"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //1.根據classpath下是否存在(ConfigurableWebApplicationContext)判斷是否要啓動一個web applicationContext this.webApplicationType = WebApplicationType.deduceFromClasspath(); //2.SpringFactoriesInstances加載classpath下全部可用的ApplicationContextInitializer setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); //3.SpringFactoriesInstances加載classpath下全部可用的ApplicationListener setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }
調用run()方法執行的過程主要分爲如下幾步:spring
/** * 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 stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); //1.遍歷SpringApplication初始化過程當中加載的SpringApplicationRunListeners SpringApplicationRunListeners listeners = getRunListeners(args); //2.調用starting()監聽SpringApplication的啓動 listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //3.加載SpringBoot配置環境 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); //4.設置banner屬性 Banner printedBanner = printBanner(environment); //5.建立ConfigurableApplicationContext(應用配置上下文) context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); //6.將listeners、environment、applicationArguments、banner等重要組件與上下文對象關聯 prepareContext(context, environment, listeners, applicationArguments, printedBanner); //7.實例化bean 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; }
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args)); }
public void starting() { //遍歷全部的SpringApplicationRunListener,調用starting()方法監聽SpringApplication的啓動 for (SpringApplicationRunListener listener : this.listeners) { listener.starting(); } }
加載SpringBoot配置環境(configurableEnvironment),若是是經過web容器發佈,會加載StandardEnvironment。將配置文件(Environment)加入到監聽器對象中(SpringApplicationRunListeners)tomcat
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // Create and configure the environment //若是environment不爲空直接返回 || 若是是web環境則直接實例化StandardServletEnvironment類 || 若是不是web環境則直接實例化StandardEnvironment類 ConfigurableEnvironment environment = getOrCreateEnvironment(); //配置環境信息 configureEnvironment(environment, applicationArguments.getSourceArgs()); //通知全部的監聽者,環境已經準備好了 listeners.environmentPrepared(environment); bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } ConfigurationPropertySources.attach(environment); return environment; }
private Banner printBanner(ConfigurableEnvironment environment) { //若是未開啓banner打印直接返回 if (this.bannerMode == Banner.Mode.OFF) { return null; } //建立ResourceLoader對象 ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader : new DefaultResourceLoader(getClassLoader()); //建立SpringApplicationBannerPrinter,該對象用來打印banner SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner); //若是bannerMode模式爲LOG,則將bannner打印到log文件中 if (this.bannerMode == Mode.LOG) { return bannerPrinter.print(environment, this.mainApplicationClass, logger); } //打印banner到控制檯 return bannerPrinter.print(environment, this.mainApplicationClass, System.out); }
在SpringBoot中,應用類型分爲三類springboot
public enum WebApplicationType { /** * The application should not run as a web application and should not start an * embedded web server. */ // 應用程序不是web應用,也不該該用web服務器去啓動 NONE, /** * The application should run as a servlet-based web application and should start an * embedded servlet web server. */ //應用程序應做爲基於servlet的web應用程序運行,並應啓動嵌入式servlet web(tomcat)服務器 SERVLET, /** * The application should run as a reactive web application and should start an * embedded reactive web server. */ //應用程序應做爲 reactive web應用程序運行,並應啓動嵌入式 reactive web服務器。 REACTIVE; }
根據webEnvironment是不是web環境建立默認的contextClass,AnnotationConfigEnbeddedWebApplicationContext(經過掃描全部註解類來加載bean)和ConfigurableWebApplicationContext),最後經過BeanUtils實例化上下文對象,並返回。服務器
/** * Strategy method used to create the {@link ApplicationContext}. By default this * method will respect any explicitly set application context or application context * class before falling back to a suitable default. * @return the application context (not yet refreshed) * @see #setApplicationContextClass(Class) */ protected ConfigurableApplicationContext createApplicationContext() { //根據webEnvironment是不是web環境建立默認的contextClass Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { switch (this.webApplicationType) { case SERVLET: //AnnotationConfigServletWebServerApplicationContext contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS); break; case REACTIVE: //AnnotationConfigReactiveWebServerApplicationContext contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; default: //AnnotationConfigApplicationContext contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass", ex); } } //BeanUtils實例化上下文對象 return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); }
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { //設置上下文的environment context.setEnvironment(environment); //應用上下文後處理 postProcessApplicationContext(context); //在context refresh以前,對其應用ApplicationContextInitializer applyInitializers(context); //上下文準備 listeners.contextPrepared(context); //打印啓動日誌和啓動應用的profile if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); //向beanFactory註冊單例bean:命令行參數bean beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { //向beanFactory註冊單例bean:banner bean beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } // Load the sources //獲取SpringApplication的primarySources屬性 Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); //將bean加載到應用上下文 load(context, sources.toArray(new Object[0])); //向上下文添加ApplicationListener,並廣播ApplicationPreparedEvent事件 listeners.contextLoaded(context); }