Spring Boot 2.x 源码系列1-spring boot 启动流程

一、前沿

Spring Boot 是 Spring 的衍生产品,是基于Spring4的条件注册的一套快速开发整合包,它实现了自动配置降低了项目搭建的复杂度。但是Spring Boot本身并不提供Spring框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于Spring框架的应用程序,是用于提升Spring开发者体验的工具

Spring Boot 集成了大量常用的第三方库配置(例如Jackson、JDBC、Mongo、Redis、MQ、Mail等等),Spring Boot应用中这些第三方库几乎可以做到零配置的开箱即用(out-of-the-box),大部分的Spring Boot应用都只需要非常少量的配置代码即可,开发者能够更加专注于业务逻辑。

Spring Boot 集成了 tomcat 和 jetty WEB 服务器,Spring Boot 默认使用 tomcat 做 WEB 服务器,如果你使用的是 tomcat,则不需要单独配置 tocmat 了

Spring Boot 和 Spring 的区别

1)、Spring Boot 实现了自动配置,例如 Mybatis、Redis、JDBC、MQ、Mongo等

2)、Spring Boot 集成了 WEB 服务器,例如 Tomcat 和 Jetty

3)、Spring Boot 实现了配置文件的环境隔离,通过 spring.profiles.active 属性可以指定加载某个环境的下的配置文件

接下来我们探究一下 Spring Boot 的启动流程

二、启动流程图

Spring Boot 启动流程图如下:

详细的流程图如下:

从上图中可知,Spring Boot 启动共分为三个部分

1)、SpringApplication 的初始化模块,即配置了 source、web环境、初始化构造器、应用监听器和main方法所在类

2)、SpringApplication 的启动模块,即启动了流程的监听器、加载环境配置和创建上下文 ApplicationContext

3)、Spring Boot 的自动化配置模块,即调用 ApplicationContext 的 refresh 方法刷新上下文,完成了所有非lazy的Singleton Bean 的加载,其中包括最核心的 Spring Boot 的各种自动化配置类的加载

下面从源码角度分别对这三个部分展开详细分析

三、Spring Boot 启动

Spring Boot 启动分为初始化模块、启动模块和自动化配置模块三部分

3.1 初始化模块

Spring Boot 的入口在带有 @SpringBootApplication 注解的 main 方法中,在 main 方法中主要做了两件事

1)、构造 SpringApplication,即 new SpringApplication(),进行初始化模块操作

2)、调用 SpringApplication 的 run 方法完成 Spring Boot 的启动

代码如下:

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ImportResource;
import org.springframework.core.env.ConfigurableEnvironment;

/**
 * springBoot主入口
 *
 * @create 2019-12-31 11:36
 **/
@SpringBootApplication(scanBasePackages = "com.springboot.demo")
@MapperScan("com.springboot.db.mapper")
@ImportResource("classpath:config/application.xml")
public class MainApplication {
    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(MainApplication.class);
        ConfigurableApplicationContext ctx = application.run(args);
    }
}

从启动类中可知 @SpringBootApplication 注解很关键,下面看一下其作用,源码如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
		@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM,
				classes = AutoConfigurationExcludeFilter.class) })
@ConfigurationPropertiesScan
public @interface SpringBootApplication {

	// 排除特定的自动配置类
	@AliasFor(annotation = EnableAutoConfiguration.class)
	Class<?>[] exclude() default {};

	// 排除特定的自动配置类的名称
	@AliasFor(annotation = EnableAutoConfiguration.class)
	String[] excludeName() default {};

	// 扫描 scanBasePackages 配置的包下的组件,比如 @Service、@Component 等
	// 默认扫描主类所在包下的所有组件
	@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
	String[] scanBasePackages() default {};

	// 扫描 scanBasePackageClasses 配置的类所在的包组件
	@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
	Class<?>[] scanBasePackageClasses() default {};

	// 是否代理 bean,默认是 true
	@AliasFor(annotation = Configuration.class)
	boolean proxyBeanMethods() default true;

}

@SpringBootApplication 注解中有包含三个核心注解,分别如下:

@SpringBootConfiguration(内部是 @Configuration 注解) : 这个注解和 @Configuration 注解的作用一样,用来表示被标注的类是一个配置类,会将被标注的类中的所有带 @Bean 注解修饰的方法添加到 Spring 容器中,实例的名称默认是方法名,Spring 会创建出对应的实例 Bean

@EnableAutoConfiguration:开启了 SpringBoot 自动配置,在程序启动时会自动加载 SpringBoot 的自动默认配置,如果有对一些参数进行配置(比如在配置文件中修改了配置属性值),则会在程序启动时或调用时进行追加或者覆盖

@ComponentScan:包扫描注解,默认扫描主类包路径下的组件类,所以最好将启动类 MainApplication 存放在根路径下,这里可以配置自己想要扫描的任何包的路径

@ImportResource:引入某路径下的资源文件

下面看构造 SpringApplication 的具体过程,入口在 SpringApplication 的构造方法,源码如下:

	// 1、 SpringApplication 的构造方法
	public SpringApplication(Class<?>... primarySources) {
		this(null, primarySources);
	}


	// 2、 SpringApplication 的构造方法
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		// 配置resource
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		// 配置是否是web环境,web环境的话后续需要启动web服务器,比如 tomcat
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		// 创建初始化构造器,加载所有 MATE-INF/spring.factories 文件中的配置,所有配置缓存到 cache 对象中
		// 根据 ApplicationContextInitializer 类型从 cache 中获取所有的初始化器,通过反射创建所有初始化器实例
		setInitializers((Collection) getSpringFactoriesInstances(
				ApplicationContextInitializer.class));
		// 根据 ApplicationListener 类型从 cache 中获取所有的应用监听器,通过反射创建所有应用监听器实例
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		// 设置应用main方法所在的主类
		this.mainApplicationClass = deduceMainApplicationClass();
	}


	// 3、SpringApplication 的 getSpringFactoriesInstances 方法
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
		// 从 MATE-INF/Spring.factories 文件中加载指定 type 类型的 Bean
		return getSpringFactoriesInstances(type, new Class<?>[] {});
	}

	// 4、SpringApplication 的 getSpringFactoriesInstances 方法
	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
			Class<?>[] parameterTypes, Object... args) {
		// 默认是当前线程上下文的 ClassLoader,resourceLoader 可以指定 ClassLoader
		ClassLoader classLoader = getClassLoader();
		// Use names and ensure unique to protect against duplicates
		// SpringFactoriesLoader 加载所有 MATE-INF/Spring.factories 文件中的配置
		Set<String> names = new LinkedHashSet<>(
				SpringFactoriesLoader.loadFactoryNames(type, classLoader));
		// 反射创建指定 Type 类型的所有 Bean
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
				classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

MATE-INF/Spring.factories 文件中的配置内容如下:

上述代码逻辑相对简单,代码注释已经讲得非常清楚了,这里不在做过多赘述了

3.2 启动模块

Spring Boot 的启动从 SpringApplication 的 run 方法开始,源码如下:

	// SpringApplication 的 run 方法
	public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		// 构建 StopWatch,记录启动时间
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		// 配置 Headless ,默认是 true
		configureHeadlessProperty();
		// 通过读取缓存 cache 创建 SpringApplicationRunListener 实例
		SpringApplicationRunListeners listeners = getRunListeners(args);
		// 启动流程的监听器,即发布 ApplicationStartingEvent 事件
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			// 准备环境 ConfigurableEnvironment,即配置环境
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			// 配置 spring.beaninfo.ignore 属性值,默认是 true
			configureIgnoreBeanInfo(environment);
			// 配置 Banner,这里可以自定义 Spring Boot 启动时输出的 banner 信息
			Banner printedBanner = printBanner(environment);
			// 创建 ApplicationContext 上下文,默认是 AnnotationConfigApplicationContext
			context = createApplicationContext();
			// 创建 SpringBootExceptionReporter 实例
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			// 准备上下文,即配置上下文
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
			// 刷新上下文,加载所有非lazy的Singleton Bean,Spring Boot 的自动配置就是在这里完成的
			refreshContext(context);
			// 刷新上下文后的操作,这里是空实现,可以作为扩展
			afterRefresh(context, applicationArguments);
			// 记录总启动时间
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			// 发布 ApplicationStartedEvent 事件
			listeners.started(context);
			// 调用命令的回调方法
			callRunners(context, applicationArguments);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, listeners);
			throw new IllegalStateException(ex);
		}

		try {
			// 发布 ApplicationReadyEvent 事件
			listeners.running(context);
		}
		catch (Throwable ex) {
			handleRunFailure(context, ex, exceptionReporters, null);
			throw new IllegalStateException(ex);
		}
		return context;
	}

run 方法中主要实现了以下逻辑:

1)、准备环境 ConfigurableEnvironment,即配置环境

2)、创建 ApplicationContext 上下文

3)、准备上下文,即配置上下文

4)、刷新上下文,加载所有非lazy的Singleton Bean,Spring Boot 的自动配置就是在这里完成的

下面着重分析前三步骤,因为第四个步骤在自动配置模块讲解

3.2.1 配置环境 ConfigurableEnvironment

配置环境入口在 SpringApplication 的 prepareEnvironment 方法,源码如下:

	// 1、SpringApplication 的 prepareEnvironment 方法
	private ConfigurableEnvironment prepareEnvironment(
			SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments) {
		// Create and configure the environment
		// 创建配置环境,默认是 StandardEnvironment,
		ConfigurableEnvironment environment = getOrCreateEnvironment();
		// 设置环境配置,即设置 ConversionService、PropertySources 和 Profiles(spring.profiles.active 指定的环境文件)
		configureEnvironment(environment, applicationArguments.getSourceArgs());
		// 发布 ApplicationEnvironmentPreparedEvent 事件
		listeners.environmentPrepared(environment);
		// 绑定环境到 SpringApplication 应用上
		bindToSpringApplication(environment);
		if (!this.isCustomEnvironment) {
			// 非自定义环境时,使用环境转换器转换环境
			environment = new EnvironmentConverter(getClassLoader())
					.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
		}
		// PropertySource 设置 SpringConfigurationPropertySources
		ConfigurationPropertySources.attach(environment);
		return environment;
	}


	// 2、SpringApplication 的 getOrCreateEnvironment 方法
	private ConfigurableEnvironment getOrCreateEnvironment() {
		if (this.environment != null) {
			return this.environment;
		}
		// 根据 webApplicationType 配置决定环境对象,默认是 StandardEnvironment
		switch (this.webApplicationType) {
		case SERVLET:
			return new StandardServletEnvironment();
		case REACTIVE:
			return new StandardReactiveWebEnvironment();
		default:
			return new StandardEnvironment();
		}
	}

在加载配置环境的过程中会判断是否是 web 容器启动,如果是容器启动会加载 StandardServletEnvironment,其继承了 ConfigurableEnvironment,结构图如下:

从上图可以看出,xxxEnvironment 最终都实现了PropertyResolver 接口,而 PropertyResolver 接口是用于解析任何基础源的属性的接口

我们平时通过 Environment 对象获取配置文件中指定 Key 对应的 value 时,就是调用了 PropertyResolver 接口的 getProperty 方法

3.2.2 创建上下文 ApplicationContext

创建上下文 ApplicationContext 入口在 SpringApplication 的 createApplicationContext 方法,源码如下:

	// SpringApplication 的 createApplicationContext 方法
	protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				// 根据 webApplicationType 决定实例上下文对象,默认是 AnnotationConfigApplicationContext
				switch (this.webApplicationType) {
				case SERVLET:
					// web 环境对应的是 AnnotationConfigServletWebServerApplicationContext 上下文
					contextClass = Class.forName(DEFAULT_SERVLET_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);
			}
		}
		// 使用构造反射法创建 ApplicationContext 实例
		return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
	}

3.2.3  配置上下文

配置上下文入口在 SpringApplication 的 prepareContext 方法,源码如下:

	// SpringApplication 的 prepareContext 方法
	private void prepareContext(ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		// 上下文配置环境
		context.setEnvironment(environment);
		// BeanFactory 设置 beanNameGenerator 和 ConversionService,context 设置 ResourceLoader 或者 ClassLoader
		postProcessApplicationContext(context);
		// 调用所有 ApplicationContextInitializer 的 initialize 方法
		applyInitializers(context);
		// 发布 ApplicationContextInitializedEvent 事件
		listeners.contextPrepared(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// Add boot specific singleton beans
		// 获取 BeanFactory 并注册 ApplicationArguments、Banner等
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
		if (beanFactory instanceof DefaultListableBeanFactory) {
			((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
		}
		if (this.lazyInitialization) {
			context.addBeanFactoryPostProcessor(
					new LazyInitializationBeanFactoryPostProcessor());
		}
		// Load the sources
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		// 加载所有source下的所有 Bean 定义 BeanDefinition
		load(context, sources.toArray(new Object[0]));
		// 实现了 ApplicationContextAware 的设置 ApplicationContext,并发布 ApplicationPreparedEvent 事件
		listeners.contextLoaded(context);
	}

配置上下文主要实现了以下逻辑:

1)、上下文配置环境

2)、BeanFactory 的一些常规属性设置

3)、调用所有初始化器 ApplicationContextInitializer 的 initialize 方法

4)、加载所有source下的所有 Bean 定义 BeanDefinition,即配置文件中的所有bean配置都转化为 BeanDefinition

5)、设置 ApplicationContext,并发布 ApplicationPreparedEvent 事件

3.3  刷新上下文 refresh

Spring Boot 完成了创建上下文 ApplicationContext 和 配置上下文之后,接下来的工作就是刷新上下文了,Spring Boot 的刷新上下文除了兼具Spring所有功能之外,主要完成了以下工作:

1)、创建了web服务器

2)、加载所有非lazy的Singleton Bean

3)、加载自动化配置

4)、启动 WEB 服务器

下面我们以 WEB 环境为例,通过源码来探讨其过程,刷新上下文入口在 SpringApplication 的 refresh 方法,源码如下:

	// 1、SpringApplication 的 refresh 方法
	protected void refresh(ApplicationContext applicationContext) {
		Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
		// web环境的话,这里调用的是 ServletWebServerApplicationContext 的 refresh 方法
		((AbstractApplicationContext) applicationContext).refresh();
	}


	// 2、ServletWebServerApplicationContext 的 refresh 方法
	@Override
	public final void refresh() throws BeansException, IllegalStateException {
		try {
			// 调用 AbstractApplicationContext 的 refresh 方法
			super.refresh();
		}
		catch (RuntimeException ex) {
			stopAndReleaseWebServer();
			throw ex;
		}
	}

AbstractApplicationContext 的 refresh 方法实现的所有逻辑我们已经在 Spring源码系列之ApplicationContext refresh 刷新 中讲解过了,这里不在重复分析和Spring做了同样事情的步骤,只分析 Spring Boot 主要实现的自己关键业务步骤,即 onRefresh 方法、finishBeanFactoryInitialization 方法 finishRefresh 方法,下面分别介绍

3.3.1 onRefresh 方法

Spring Boot 的 onRefresh 方法主要实现了创建WEB服务器并启动Server和Service功能,入口在 ServletWebServerApplicationContext 的 onRefresh 方法,源码如下:

	// 1、ServletWebServerApplicationContext 的 onRefresh 方法
	@Override
	protected void onRefresh() {
		// 初始化上下文主题资源
		super.onRefresh();
		try {
			// 创建WEB服务器
			createWebServer();
		}
		catch (Throwable ex) {
			throw new ApplicationContextException("Unable to start web server", ex);
		}
	}


	// 2、ServletWebServerApplicationContext 的 createWebServer 方法
	private void createWebServer() {
		WebServer webServer = this.webServer;
		ServletContext servletContext = getServletContext();
		if (webServer == null && servletContext == null) {
			// 获取 ServletWebServerFactory 实例
			ServletWebServerFactory factory = getWebServerFactory();
			// getSelfInitializer 方法初始化 ServletContext
			// ServletWebServerFactory(TomcatServletWebServerFactory) 的 getWebServer 方法获取 web 服务器
			this.webServer = factory.getWebServer(getSelfInitializer());
		}
		else if (servletContext != null) {
			try {
				getSelfInitializer().onStartup(servletContext);
			}
			catch (ServletException ex) {
				throw new ApplicationContextException("Cannot initialize servlet context",
						ex);
			}
		}
		// 初始化属性资源
		initPropertySources();
	}


	// 3、ServletContextInitializer 的 getSelfInitializer 方法
	private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
		return this::selfInitialize;
	}

	// 4、ServletContextInitializer 的 getSelfInitializer 方法
	private void selfInitialize(ServletContext servletContext) throws ServletException {
		// ServletContext 属性设置
		prepareWebApplicationContext(servletContext);
		// BeanFactory 中注册 application 类型的 scope
		registerApplicationScope(servletContext);
		WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(),
				servletContext);
		// ServletContextInitializer 的 onStartup 方法做初始化操作,
		// 例如 DispatcherServletRegistrationBean 的 onStartup 方法实现了 ServletContext 增加 DispatcherServlet,
		// TomcatEmbeddedContext 增加 dispatcherServlet 中配置的 url 与 Wrapper 的映射关系
		// FilterRegistrationBean 的 onStartup 方法实现了 ServletContext 增加 CharacterEncodingFilter、HiddenHttpMethodFilter、FormContentFilter
		// 和 RequestContextFilter,TomcatEmbeddedContext 增加 characterEncodingFilter、hiddenHttpMethodFilter、formContentFilter 和 requestContextFilter
		// 中配置的 url 组成的FilterMap
		// 总结:ServletContext 绑定 Servlet 与 Filter 的实现
		for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
			beans.onStartup(servletContext);
		}
	}


	// 5、TomcatServletWebServerFactory 的 getWebServer 方法
	@Override
	public WebServer getWebServer(ServletContextInitializer... initializers) {
		Tomcat tomcat = new Tomcat();
		File baseDir = (this.baseDirectory != null) ? this.baseDirectory
				: createTempDir("tomcat");
		tomcat.setBaseDir(baseDir.getAbsolutePath());
		Connector connector = new Connector(this.protocol);
        // Service 与 Connector 绑定,此时 Connector 不会启动
		tomcat.getService().addConnector(connector);
		// Connector 的 bindOnInit 属性值决定了是否立即打开 Socket 绑定套接字,如果是应用启动较慢,这里建议设置为 false,即应用启动之后再打开 Socket 绑定套接字监听
		customizeConnector(connector);
		tomcat.setConnector(connector);
		tomcat.getHost().setAutoDeploy(false);
		// Engine 中的 Pipeline 与 Valve 关系绑定
		configureEngine(tomcat.getEngine());
		for (Connector additionalConnector : this.additionalTomcatConnectors) {
			// Service 与 Connector 绑定
			tomcat.getService().addConnector(additionalConnector);
		}
		// 创建并初始化设置 TomcatEmbeddedContext
		prepareContext(tomcat.getHost(), initializers);
		// 初始化 tomcat web 服务器,并返回 TomcatWebServer
		return getTomcatWebServer(tomcat);
	}

	// 6、TomcatServletWebServerFactory 的 getTomcatWebServer 方法
	protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
		return new TomcatWebServer(tomcat, getPort() >= 0);
	}

	// 7、TomcatWebServer 的构造方法
	public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
		Assert.notNull(tomcat, "Tomcat Server must not be null");
		this.tomcat = tomcat;
		this.autoStart = autoStart;
		// 启动 tomcat 服务
		initialize();
	}

	// 8、TomcatWebServer 的 initialize 方法
	private void initialize() throws WebServerException {
		logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
		synchronized (this.monitor) {
			try {
				addInstanceIdToEngineName();

				Context context = findContext();
				context.addLifecycleListener((event) -> {
					if (context.equals(event.getSource())
							&& Lifecycle.START_EVENT.equals(event.getType())) {
						// Remove service connectors so that protocol binding doesn't
						// happen when the service is started.
						// 这里将 Service 和 Connector 关系移除了,防止启动 Service 时将 Connector 启动,这里就是将tomcat的真正启动放在了应用启动之后,提高性能
						removeServiceConnectors();
					}
				});

				// Start the server to trigger initialization listeners
				// 启动 tomcat 服务器,并触发初始化监听器
				// 注意这里只启动了 Server 和 Service,而没有启动 Connector 和打开 Socket 套接字,等到应用启动完成之后再启动 Connector 和 Socket
				this.tomcat.start();

				// We can re-throw failure exception directly in the main thread
				rethrowDeferredStartupExceptions();

				try {
					ContextBindings.bindClassLoader(context, context.getNamingToken(),
							getClass().getClassLoader());
				}
				catch (NamingException ex) {
					// Naming is not enabled. Continue
				}

				// Unlike Jetty, all Tomcat threads are daemon threads. We create a
				// blocking non-daemon to stop immediate shutdown
				startDaemonAwaitThread();
			}
			catch (Exception ex) {
				stopSilently();
				throw new WebServerException("Unable to start embedded Tomcat", ex);
			}
		}
	}

onRefresh 方法主要实现了以下逻辑:

1)、创建 ServletContext ,并绑定 DispatcherServlet 和 Filter

2)、创建 tomcat web 服务器并启动 tomcat,这里为了性能优化,通过设置 Connector 的 bindOnInit 属性为false来禁止完全启动 tomcat,这里只启动了 Tomcat 的 Server 和 Service,待应用启动好之后再启动 tomcat,即启动 Connector 和 Socket 套接字

3.3.2  finishBeanFactoryInitialization 方法

finishBeanFactoryInitialization 方法同样加载了所有非lazy的Singleton Bean,Spring Boot 正是在此过程中实现了 自动配置类的加载,这个是 Spring Boot 的核心,我们在下一节对其单独分析,这里不在赘述了

3.3.3  finishRefresh 方法

Spring Boot 的 finishRefresh 方法除了实现Spring原有方法的逻辑之外,还实现了完全启动WEB服务器(启动 Tomcat 的 Connector 和 Socket 套接字)功能,入口在 ServletWebServerApplicationContext 的 finishRefresh 方法,源码如下:

	// 1、ServletWebServerApplicationContext 的 finishRefresh 方法
	@Override
	protected void finishRefresh() {
		// Spring 原有方法的逻辑,这里不在分析
		super.finishRefresh();
		// 启动web服务器
		WebServer webServer = startWebServer();
		if (webServer != null) {
			// 发布 ServletWebServerInitializedEvent 事件,即宣告web应用启动完成
			publishEvent(new ServletWebServerInitializedEvent(webServer, this));
		}
	}


	// 2、ServletWebServerApplicationContext 的 startWebServer 方法
	private WebServer startWebServer() {
		WebServer webServer = this.webServer;
		if (webServer != null) {
			// 调用 WebServer(TomcatWebServer) 的 start 方法启动服务
			webServer.start();
		}
		return webServer;
	}


	// 3、TomcatWebServer 的 start 方法
	@Override
	public void start() throws WebServerException {
		//  加对象锁,防止重复启动
		synchronized (this.monitor) {
			if (this.started) {
				return;
			}
			try {
				// connector 与 service 重新绑定,并启动 Connector,从而打开 Socket 套接字 完成 tomcat 的整个启动
				addPreviouslyRemovedConnectors();
				Connector connector = this.tomcat.getConnector();
				if (connector != null && this.autoStart) {
					// 加载 Servlet
					performDeferredLoadOnStartup();
				}
				checkThatConnectorsHaveStarted();
				this.started = true;
				logger.info("Tomcat started on port(s): " + getPortsDescription(true)
						+ " with context path '" + getContextPath() + "'");
			}
			catch (ConnectorStartFailedException ex) {
				stopSilently();
				throw ex;
			}
			catch (Exception ex) {
				throw new WebServerException("Unable to start embedded Tomcat server",
						ex);
			}
			finally {
				Context context = findContext();
				ContextBindings.unbindClassLoader(context, context.getNamingToken(),
						getClass().getClassLoader());
			}
		}
	}


	// 4、TomcatWebServer 的 addPreviouslyRemovedConnectors 方法
	private void addPreviouslyRemovedConnectors() {
		Service[] services = this.tomcat.getServer().findServices();
		for (Service service : services) {
			Connector[] connectors = this.serviceConnectors.get(service);
			if (connectors != null) {
				for (Connector connector : connectors) {
					// connector 与 service 绑定,并启动 Connector,从而打开 Socket 套接字 完成 tomcat 的整个启动
					service.addConnector(connector);
					if (!this.autoStart) {
						stopProtocolHandler(connector);
					}
				}
				// 启动 tomcat 完成之后,移除缓存
				this.serviceConnectors.remove(service);
			}
		}
	}

finishRefresh 方法主要实现了完全启动 Tomcat 服务器的功能,具体启动的是 Connector,进而打开 Socket 套接字来完成 tomcat 的整个启动

四、总结

Spring Boot 的启动过程不是特别复杂,其中最难理解的就是 Tomcat 的启动,Tomcat 的启动分为了两部分,在 onRefresh 方法时启动了 Tomcat 的 Server 和 Service,而在 finishRefresh 方法中启动了 Tomcat 的 Connector,从而达到了待应用启动之后再完全启动 Tomcat 服务器的目的,这样做是为了防止应用启动过慢导致整个服务启动慢而优化的

如果大家想学习 tomcat 的启动过程,请查看 tomcat9源码 写篇文章

猜你喜欢

转载自blog.csdn.net/ywlmsm1224811/article/details/103779773