众所周知,类上面带有@SpringBootApplication注解的类,即为springboot的启动类。一个springboot项目只能有一个启动类。我们来分析一下SpringBoot项目的启动过程,首先看看启动类里面都包含什么
@SpringBootApplication public class HelloWorldMainApplication { public static void main(String[] args) { //spring应用启动起来 SpringApplication.run(HelloWorldMainApplication.class,args); } }
从上面的代码中可以看出真正起作用的是SpringApplication.run();这个方法,下面主要分析一下这个方法。
SpringApplication初始化时主要做三件事情:
/** * 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()方法执行的过程主要分为以下几步:
/** * 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)
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中,应用类型分为三类
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); }