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のソースをチェックして記事を書いてください。