一个读者,也是我的好朋友投稿的一篇关于 SpringBoot 启动原理的文章,才大二就如此优秀,未来可期。
我一直想了解一下 SpirngBoot 的是如何启动的,我想就来写一篇关于 SpirngBoot 启动分析吧。第一次写那么高深的技术话题理解不到位的话也请多多包涵。
SpinrgBoot 2.0.2
众所周知 SpringBoot 的启动类是在一个 main 方法中调用 SpringApplication.run()
方法启动的,如:
@SpringBootApplication public class DiveInSpringBootApplication { public static void main(String[] args) { SpringApplication.run(DiveInSpringBootApplication.class, args); } }
启动顺序分析如下:
初始化阶段 -> 运行阶段
进入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(); }
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 |
WEB_ENVIRONMENT_CLASSES | {"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" } |
也就是说,有如下三种情况:
表示当前应用不是一个web应用,启动时无需加载启动内嵌的 web 服务器。
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实现类,并排序集合。具体实现方法如下:
SpringFactoriesLoader.loadFactoryNames
来扫描 META-INF/spring.factories
下符合 ApplicationContextInitializer 类型的资源名称。 META-INF/spring.factories
下找到的资源信息 @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
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
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; }
整个 SpringApplication 围绕着 run 这个方法并分为两个小阶段:
SpringApplication
运行监听器,并监听 Spring Boot
事件 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); .... } }
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 实现类,并排序集合。具体实现方法如下:
SpringFactoriesLoader.loadFactoryNames
来扫描 META-INF/spring.factories
下符合 SpringApplicationRunListeners 类型的资源名称。 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
事件。
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
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 过程已经分析完毕:我们来总结一下: