众所周知 SpringBoot 的启动类是在一个 main 方法中调用SpringApplication.run()方法启动的,如:
@SpringBootApplication public class DiveInSpringBootApplication { public static void main(String[] args) { SpringApplication.run(DiveInSpringBootApplication.class, args); } }
启动顺序分析如下:
初始化阶段 -> 运行阶段
1. 初始化阶段
进入run方法中,SpringApplication.run()会先为其创建一个 SpringApplication 对象:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { //加载应用资源(URL资源、File资源、ClassPath资源) this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); // primarySources 为 run 方法传入的引导类 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //推断Web应用类型 this.webApplicationType = deduceWebApplicationType(); //加载应用上下文(初始化Initializers) setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); //加载应用事件监听器(初始化ApplicationListener) setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //推断引导类 this.mainApplicationClass = deduceMainApplicationClass(); } Step1. 通过deduceWebApplicationType()来推断我们Web类型应用 private WebApplicationType deduceWebApplicationType() { //根据当前应用的ClassPath中是否存在相关实现类来推断Web类型 if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null) && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) { return WebApplicationType.REACTIVE; } for (String className : WEB_ENVIRONMENT_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; } } return WebApplicationType.SERVLET; }
看看使用到的 3 个常量值:
常量值
应用类型
REACTIVE_WEB_ENVIRONMENT_CLASS org.springframework.web.reactive.DispatcherHandler MVC_WEB_ENVIRONMENT_CLASS org.springframework.web.servlet.DispatcherServlet EB_ENVIRONMENT_CLASSES {"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" }
也就是说,有如下三种情况:
1. 如果应用程序中存在org.springframework.web.reactive.DispatcherHandler 这个类,则表示是一个响应式 web 应用,项目在启动时,需要去加载启动内嵌的响应式 web 服务器。
2. 如果应用程序中既不存在 javax.servlet.Servlet 类,也不存在
org.springframework.web.context.ConfigurableWebApplicationContext 这个类,则表示当前应用不是一个web应用,启动时无需加载启动内嵌的 web 服务器。
3. 除上述两种情况外,则表示当前应用是一个 servlet 的 web 应用,启动时需要加载启动内嵌的 servlet 的 web 服务器(比如 Tomcat )。
Step2. 如何加载应用上下文初始器(初始化 Initializers )
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
利用Spirng的工厂加载机制,实例化ApplicationContextInitializer实现类,并排序集合。具体实现方法如下:
1.通过SpringFactoriesLoader.loadFactoryNames来扫描META-INF/spring.factories下符合 ApplicationContextInitializer 类型的资源名称。
2. 实例化所有在META-INF/spring.factories下找到的资源信息
3. 对实例化的资源信息进行优先级顺序排序,或通过@order注解和Ordered接口进行排序
# Application Context Initializers org.springframework.context.ApplicationContextInitializer=\ #Spring容器的常见的错误配置警告 org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ #设置Spring应用上下文ID org.springframework.boot.context.ContextIdApplicationContextInitializer,\ org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer Step3. 加载应用事件监听器( ApplicationListener ) private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<>( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
利用 Spring 工厂加载机制,实例化 ApplicationListene r实现类,并排序对象集合,具体方法跟上面初始化Initializers类似,不赘述。
# Application Listeners org.springframework.context.ApplicationListener=\ #Spring应用上下文加载完成之后清除缓存 org.springframework.boot.ClearCachesApplicationListener,\ #父容器关闭时通知各个子容器关闭, org.springframework.boot.builder.ParentContextCloserApplicationListener,\ #文件编码 org.springframework.boot.context.FileEncodingApplicationListener,\ #控制台彩色输出 org.springframework.boot.context.config.AnsiOutputApplicationListener,\ #外部化配置 管理factories或者YMAL文件 org.springframework.boot.context.config.ConfigFileApplicationListener,\ #将指定事件广播给指定的监听器 org.springframework.boot.context.config.DelegatingApplicationListener,\ #将需要输出的日志打印到指定的级别 DEBUG INFO ERROR org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\ #初始化日志系统 org.springframework.boot.context.logging.LoggingApplicationListener,\ #控制可执行Spirng文件版本 org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener Step4. 推断引导类 private Class<?> deduceMainApplicationClass() { try { StackTraceElement[] stackTrace = new RuntimeException().getStackTrace(); for (StackTraceElement stackTraceElement : stackTrace) { //根据 Main 线程执行堆栈来判断实际的引导类。 if ("main".equals(stackTraceElement.getMethodName())) { return Class.forName(stackTraceElement.getClassName()); } } } catch (ClassNotFoundException ex) { // Swallow and continue } return null; }
2. 运行阶段
整个 SpringApplication 围绕着 run 这个方法并分为两个小阶段:
1. 加载SpringApplication运行监听器,并监听Spring Boot事件
2. 创建 Spring 应用上下文和创建 Environment
public ConfigurableApplicationContext run(String... args) { //记录运行时间 StopWatch stopWatch = new StopWatch(); stopWatch.start(); //Spring 应用的上下文 ConfigurableApplicationContext context = null; //记录启动期间的错误 Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); //配置文件加载及优先级判断 configureHeadlessProperty(); //获取SpringApplicationRunListeners SpringApplicationRunListeners listeners = getRunListeners(args); //加载运行监听器 listeners.starting(); try { //创建ApplicationArguments对象 ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); //加载属性配置 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); //打印Banner Banner printedBanner = printBanner(environment); //创建应用上下文 context = createApplicationContext(); //实例化SpringBootExceptionReporter用于报告启动过程错误。 exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); //初始化应用上下文 prepareContext(context, environment, listeners, applicationArguments, printedBanner); //刷新应用上下文(IOC容器的准备,初始化Bean) refreshContext(context); //应用上下刷新完成之后 afterRefresh(context, applicationArguments); stopWatch.stop(); //启动日志记录器 if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } //启动运行监听器 listeners.started(context); //启动后需要的操作 callRunners(context, applicationArguments); .... } } Step1. 加载SpringApplication运行监听器 private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances( SpringApplicationRunListener.class, types, this, args)); }
利用 Spirng 的工厂加载机制,实例化 SpringApplicationRunListeners 实现类,并排序集合。具体实现方法如下:
1.通过SpringFactoriesLoader.loadFactoryNames来扫描META-INF/spring.factories下符合 SpringApplicationRunListeners 类型的资源名称。
2. 实例化所有在META-INF/spring.factories下找到的资源信息
# Run Listeners org.springframework.boot.SpringApplicationRunListener=\ org.springframework.boot.context.event.EventPublishingRunListenr 由此可见就只有EventPublishingRunListenr一个实现类 public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered { private final SpringApplication application; private final String[] args; private final SimpleApplicationEventMulticaster initialMulticaster; public EventPublishingRunListener(SpringApplication application, String[] args) { this.application = application; this.args = args; //实例化SimpleApplicationEventMulticaster事件发布者 this.initialMulticaster = new SimpleApplicationEventMulticaster(); //以迭代的方法逐一进行ApplicationListener的监听 for (ApplicationListener<?> listener : application.getListeners()) { this.initialMulticaster.addApplicationListener(listener); } } @Override public int getOrder() { return 0; } @Override public void starting() { this.initialMulticaster.multicastEvent( new ApplicationStartingEvent(this.application, this.args)); } @Override public void environmentPrepared(ConfigurableEnvironment environment) { this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent( this.application, this.args, environment)); } @Override public void contextPrepared(ConfigurableApplicationContext context) { } @Override public void contextLoaded(ConfigurableApplicationContext context) { for (ApplicationListener<?> listener : this.application.getListeners()) { if (listener instanceof ApplicationContextAware) { ((ApplicationContextAware) listener).setApplicationContext(context); } context.addApplicationListener(listener); } this.initialMulticaster.multicastEvent( new ApplicationPreparedEvent(this.application, this.args, context)); } @Override public void started(ConfigurableApplicationContext context) { context.publishEvent( new ApplicationStartedEvent(this.application, this.args, context)); } @Override public void running(ConfigurableApplicationContext context) { context.publishEvent( new ApplicationReadyEvent(this.application, this.args, context)); } @Override public void failed(ConfigurableApplicationContext context, Throwable exception) { ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception); if (context != null && context.isActive()) { // Listeners have been registered to the application context so we should // use it at this point if we can context.publishEvent(event); } else { // An inactive context may not have a multicaster so we use our multicaster to // call all of the context's listeners instead if (context instanceof AbstractApplicationContext) { for (ApplicationListener<?> listener : ((AbstractApplicationContext) context) .getApplicationListeners()) { this.initialMulticaster.addApplicationListener(listener); } } this.initialMulticaster.setErrorHandler(new LoggingErrorHandler()); this.initialMulticaster.multicastEvent(event); } } private static class LoggingErrorHandler implements ErrorHandler { private static Log logger = LogFactory.getLog(EventPublishingRunListener.class); @Override public void handleError(Throwable throwable) { logger.warn("Error calling ApplicationEventListener", throwable); } } }
在EventPublishingRunListener实例化的时候,会实例化一个
SimpleApplicationEventMulticaster事件发布者(它的作用就是监听容器中发布的事件,只要事件发生,就触发监听器的回调,来完成事件驱动开发),于是接下来调用listeners.starting()方法就会通过其内部的initialMulticaster属性发布
ApplicationStartingEvent事件。
Step2. 创建Spirng应用上下文
protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { //初始化阶段的推断Web类型 switch (this.webApplicationType) { case SERVLET: contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS); break; case REACTIVE: contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS); break; default: contextClass = Class.forName(DEFAULT_CONTEXT_CLASS); } } catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, " + "please specify an ApplicationContextClass", ex); } } return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass); }
根据初始化阶段的推断Web应用类型来创建对应的 ConfigurableApplicationContext 实例如果推断的为 SERVLETWeb 类型就实例化这个对象
web.servlet.context.AnnotationConfigServletWebServerApplicationContext
Step3. 创建Environment
private ConfigurableEnvironment prepareEnvironment( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // Create and configure the environment //创建 ConfigurableEnvironment 对象 ConfigurableEnvironment environment = getOrCreateEnvironment(); //配置 ConfigurableEnvironment configureEnvironment(environment, applicationArguments.getSourceArgs()); //发布 ApplicationEnvironmentPreparedEvent 事件 listeners.environmentPrepared(environment); //将 ConfigurableEnvironment 绑定到 SpringApplication 中 bindToSpringApplication(environment); if (this.webApplicationType == WebApplicationType.NONE) { environment = new EnvironmentConverter(getClassLoader()) .convertToStandardEnvironmentIfNecessary(environment); } ConfigurationPropertySources.attach(environment); return environment; }
根据初始化阶段的推断Web应用类型来创建对应的 ConfigurableEnvironment 实例。
至此整一个的 SpringBoot 过程已经分析完毕:我们来总结一下:
1. 首先初始化 SpringApplication 类,并推断 WEB 启动类型,再初始化和实现应用事件监听器,然后推断引导类。
2. 通过 SpringFactoriesLoader 加载的 SpringApplicationRunListener,调用它们的 started 方法。
3. 根据 Web 服务类型创建不同的 Spring 应用上下文,并将之前准备好的 Environment 设置给 Spring 应用上下文 ApplicationContext 使用。
4. 创建并配置当前 Spring Boot 应用将要使用的 Environment,如
applocation.properties 文件和外部配置。
5. SpirngBoot 开始启动。