Spring Boot2.xソースコードシリーズ1-SpringBootの起動プロセス

1.フロンティア

Spring Bootは、Springの派生製品です。Spring4の条件付き登録に基づく迅速な開発統合パッケージのセットです。自動構成実現し、プロジェクト構築の複雑さを軽減しますただし、Spring Boot自体は、Springフレームワークのコア機能と拡張機能を提供していません。これは、Springフレームワークに基づく新世代のアプリケーションを迅速かつ機敏に開発するためにのみ使用されます。これは、Spring開発者エクスペリエンス強化するために使用されるツールです

Spring Bootは、一般的に使用される多数のサードパーティライブラリ構成(Jackson、JDBC、Mongo、Redis、MQ、Mailなど)を統合します。SpringBootアプリケーションのこれらのサードパーティライブラリは、ほとんど使用できません。構成がゼロのボックス(すぐに使用可能)で、ほとんどのSpring Bootアプリケーションはごく少量の構成コードしか必要とせず、開発者はビジネスロジックにより集中できます。

春ブーツ統合Tomcatと桟橋WEBサーバ。春ブーツは、Tomcatを使用して、Webサーバーとしてデフォルト。Tomcatを使用している場合は、個別のconfigure tocmatする必要はありません。

SpringBootとSpringの違い

1)Spring Bootは、Mybatis、Redis、JDBC、MQ、Mongoなどの自動構成を実装します。

2)Spring Bootは、TomcatやJettyなどのWEBサーバーを統合します

3)Spring Bootは、構成ファイルの環境分離を実現し、特定の環境下の構成ファイルは、spring.profiles.activeプロパティを介してロードするように指定できます。

次に、SpringBootの起動プロセスを見てみましょう。

2.起動フローチャート

SpringBootの起動フローチャートは次のとおりです。

詳細なフローチャートは次のとおりです。

上の図からわかるように、SpringBootの起動は3つの部分に分かれています。

1)SpringApplicationの初期化モジュール、つまり、ソース、Web環境、初期化コンストラクター、アプリケーションリスナー、およびmainメソッドが構成されているクラス。

2)SpringApplicationの起動モジュール、つまり、プロセスを開始し、環境構成をロードして、コンテキストApplicationContextを作成するリスナー

3)Spring Bootの自動構成モジュール、つまりApplicationContextのrefreshメソッドが呼び出されてコンテキストが更新され、コアSpringのさまざまな自動構成クラスのロードを含むすべての非レイジーシングルトンBeanのロードが完了します。ブート

以下は、ソースコードの観点からこれら3つの部分を詳細に分析したものです。

3、SpringBootの開始

Spring Bootの起動は、初期化モジュール、起動モジュール、自動構成モジュールの3つの部分に分かれています。

3.1モジュールを初期化します

Spring Bootの入り口は、@ SpringBootApplicationアノテーションが付けられたmainメソッドにあり、2つのことが主にmainメソッドで実行されます。

1)、SpringApplication、つまり新しいSpringApplication()を構築し、初期化モジュール操作を実行します

2)、SpringApplicationのrunメソッドを呼び出して、SpringBootの起動を完了します。

コードは次のように表示されます。

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アノテーションには、次の3つのコアアノテーションが含まれています。

@SpringBootConfiguration(内部的には@Configurationアノテーション):このアノテーションは@Configurationアノテーションと同じ機能を持ち、アノテーション付きクラスが構成クラスであることを示すために使用され、アノテーション付きクラスの@Beanアノテーションで装飾されたすべてのメソッドが追加されますto 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)コンテキストを更新し、遅延していないすべてのシングルトンBeanをロードします。SpringBootの自動構成はここで行われます。

4番目のステップは自動構成モジュールで説明されているため、以下では最初の3つのステップの分析に焦点を当てます。

3.2.1ConfigurableEnvironment

構成環境のエントリは、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コンテナーが開始されているかどうかが判断されます。コンテナーが開始されると、ConfigurableEnvironmentを継承するStandardServletEnvironmentがロードされます。構造図は次のとおりです。

上の図からわかるように、xxxEnvironmentは最終的にPropertyResolverインターフェイスを実装し、PropertyResolverインターフェイスは基本的なソースのプロパティを解決するために使用されるインターフェイスです。

通常、Environmentオブジェクトを介して構成ファイルで指定されたKeyに対応する値を取得する場合、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

4)すべてのソースの下にすべてのBean定義BeanDefinitionをロードします。つまり、構成ファイル内のすべてのBean構成がBeanDefinitionに変換されます。

5)、ApplicationContextを設定し、ApplicationPreparedEventイベントを公開します

3.3更新コンテキストの更新

Spring BootがコンテキストApplicationContextと構成コンテキストの作成を完了した後、次のジョブはコンテキストを更新することです。すべてのSpring機能に加えて、SpringBootの更新コンテキストは主に次のタスクを完了します。

1)、Webサーバーが作成されます

2)すべての非レイジーシングルトン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リフレッシュで説明されています。ここでは、Springと同じことを行う手順は繰り返さず、SpringBootが主に行う主要なビジネス手順のみを分析します。実装、つまりonRefreshメソッド、finishBeanFactoryInitializationメソッドfinishRefreshメソッド、以下が導入されました

3.3.1onRefreshメソッド

Spring BootのonRefreshメソッドは、主にWEBサーバー作成し、サーバーとサービスを起動する機能を実装します。エントリは、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を起動します。パフォーマンスを最適化するために、コネクタのbindOnInitプロパティをfalseに設定して tomcatが完全に起動しないようにしますここでは、Tomcatのサーバーとサービスのみが起動します。アプリケーションの起動後、 tomcatが起動します。つまり、StartConnectorとSocketソケットです。

3.3.2finishBeanFactoryInitialization方法

finishBeanFactoryInitializationメソッドは、レイジーでないすべてのシングルトンBeanもロードします。SpringBootが自動構成クラスのロードを実装するのはこのプロセスです。これがSpringBoot のコアです。次のセクションで個別に分析します。繰り返しません。ここに。

3.3.3finishRefresh方法

Springの元のメソッドのロジックに加えて、Spring BootのfinishRefreshメソッドは、WEBサーバー完全に起動する(Tomcatのコネクタとソケットソケットを起動する)機能も実現します。エントリは、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サーバーを完全に起動する機能を実現します。具体的な起動はコネクタであり、ソケットソケットが開かれてTomcatの起動全体が完了します。

4、まとめ

SpringBootの起動プロセスは特に複雑ではありません。最も理解しにくいのはTomcatの起動です。Tomcatの起動は2つの部分に分かれています。TomcatのサーバーとサービスはonRefreshメソッドとTomcatのコネクタで起動されます。はfinishRefreshメソッドで開始されます。アプリケーションの起動後にTomcatサーバーを完全に起動するという目的を達成するために、これはアプリケーションの起動が遅くなりすぎてサービス全体の起動が遅くなるの防ぐためです。

tomcatの起動について学びたい場合は、tomcat9のソースチェックして記事を書いてください。

おすすめ

転載: blog.csdn.net/ywlmsm1224811/article/details/103779773