blog

常にSpringBootは、組み込みのtomcatが起動すると、その原理を明確に言う?

はじめに\n\n組み込みのtomcat\n開発段階では、組み込みのtomcatで十分ですが、もちろんjettyを使うこともできます。\n<>\n <groupId>;...

Nov 14, 2020 · 10 min. read
シェア

序文

内蔵 tomcat

もちろん、jettyを使うこともできます。

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.1.6.RELEASE</version> </dependency> @SpringBootApplication public class MySpringbootTomcatStarter{ public static void main(String[] args) { Long time=System.currentTimeMillis(); SpringApplication.run(MySpringbootTomcatStarter.class); System.out.println("===アプリケーションの起動に時間がかかる:"+(System.currentTimeMillis()-time)+"==="); } }

リリース制作

リリースの際、内蔵の tomcat を除外し、パッケージをタイル状に並べ、本番の tomcat にデプロイするのが現在の主流ですが、では、そのパッケージはどうすればいいのでしょうか?

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-web</artifactId>
 <exclusions>
 <exclusion>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-tomcat</artifactId>
 </exclusion>
 </exclusions>
</dependency>
<dependency>
 <groupId>javax.servlet</groupId>
 <artifactId>javax.servlet-api</artifactId>
 <version>3.1.0</version>
 <scope>provided</scope>
</dependency>
@SpringBootApplication
public class MySpringbootTomcatStarter extends SpringBootServletInitializer {
 public static void main(String[] args) {
 Long time=System.currentTimeMillis();
 SpringApplication.run(MySpringbootTomcatStarter.class);
 System.out.println("===アプリケーションの起動に時間がかかる:"+(System.currentTimeMillis()-time)+"===");
 }
 @Override
 protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
 return builder.sources(this.getClass());
 }
}

メイン機能から

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
 return run(new Class[]{primarySource}, args);
}
--ここで、runメソッドはConfigurableApplicationContext
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
 return (new SpringApplication(primarySources)).run(args);
}
public ConfigurableApplicationContext run(String... args) {
 ConfigurableApplicationContext context = null;
 Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
 this.configureHeadlessProperty();
 SpringApplicationRunListeners listeners = this.getRunListeners(args);
 listeners.starting();
 Collection exceptionReporters;
 try {
 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
 ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
 this.configureIgnoreBeanInfo(environment);
 //印刷バナー、ここでは落書きができる。logo
 Banner printedBanner = this.printBanner(environment);
 //アプリケーション・コンテキストの作成
 context = this.createApplicationContext();
 exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
 //前処理コンテキスト
 this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
 //コンテキストを更新する
 this.refreshContext(context);
 //コンテキストを更新する
 this.afterRefresh(context, applicationArguments);
 listeners.started(context);
 this.callRunners(context, applicationArguments);
 } catch (Throwable var10) {
 }
 try {
 listeners.running(context);
 return context;
 } catch (Throwable var9) {
 }
}

SpringBootでtomcatがどのように起動されるかを知りたいので、runメソッドはアプリケーションコンテキストの作成とコンテキストのリフレッシュに重点を置いています。

コンテキストの作成

//コンテナの作成
protected ConfigurableApplicationContext createApplicationContext() {
 Class<?> contextClass = this.applicationContextClass;
 if (contextClass == null) {
 try {
 switch(this.webApplicationType) {
 case SERVLET:
 // AnnotationConfigServletWebServerApplicationContext
 contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
 break;
 case REACTIVE:
 contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
 break;
 default:
 contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
 }
 } catch (ClassNotFoundException var3) {
 throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
 }
 }
 return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}

ここでは、AnnotationConfigServletWebServerApplicationContextクラスを作成します。

そして、AnnotationConfigServletWebServerApplicationContext。

クラスは ServletWebServerApplicationContext を継承しています。

そして、このクラスがAbstectApplicationContextの最終的な統合です。

リフレッシュコンテキスト

//SpringApplication.java
//コンテキストを更新する
private void refreshContext(ConfigurableApplicationContext context) {
 this.refresh(context);
 if (this.registerShutdownHook) {
 try {
 context.registerShutdownHook();
 } catch (AccessControlException var3) {
 }
 }
}
//ここでは、最終的な親クラスを直接呼び出すAbstractApplicationContext.refresh() 
protected void refresh(ApplicationContext applicationContext) {
 ((AbstractApplicationContext)applicationContext).refresh();
}
//AbstractApplicationContext.java
public void refresh() throws BeansException, IllegalStateException {
 synchronized(this.startupShutdownMonitor) {
 this.prepareRefresh();
 ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
 this.prepareBeanFactory(beanFactory);
 try {
 this.postProcessBeanFactory(beanFactory);
 this.invokeBeanFactoryPostProcessors(beanFactory);
 this.registerBeanPostProcessors(beanFactory);
 this.initMessageSource();
 this.initApplicationEventMulticaster();
 //各サブクラスのonRefreshメソッドを呼び出す。
 this.onRefresh();
 this.registerListeners();
 this.finishBeanFactoryInitialization(beanFactory);
 this.finishRefresh();
 } catch (BeansException var9) {
 this.destroyBeans();
 this.cancelRefresh(var9);
 throw var9;
 } finally {
 this.resetCommonCaches();
 }
 }
}
//ServletWebServerApplicationContext.java
//このメソッドでおなじみのthis.createWebServer謎はこれから解き明かされる。
protected void onRefresh() {
 super.onRefresh();
 try {
 this.createWebServer();
 } catch (Throwable var2) {
 }
}
//ServletWebServerApplicationContext.java
//ここでは、WebServerの作成が、まだtomcatが起動されていない、ここではServletWebServerFactoryを介して作成され、次を見て続けているServletWebServerFactory
private void createWebServer() {
 WebServer webServer = this.webServer;
 ServletContext servletContext = this.getServletContext();
 if (webServer == null && servletContext == null) {
 ServletWebServerFactory factory = this.getWebServerFactory();
 this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
 } else if (servletContext != null) {
 try {
 this.getSelfInitializer().onStartup(servletContext);
 } catch (ServletException var4) {
 }
 }
 this.initPropertySources();
}
// 
public interface ServletWebServerFactory {
 WebServer getWebServer(ServletContextInitializer... initializers);
}
// 
AbstractServletWebServerFactory
JettyServletWebServerFactory
TomcatServletWebServerFactory
UndertowServletWebServerFactory

ここで、ServletWebServerFactory インターフェースは4つの実装クラスを持っています。

そして、一般的に使われているのは2つ:

TomcatServletWebServerFactoryとJettyServletWebServerFactoryです。

//TomcatServletWebServerFactory.java
//ここで、tomcatが使用されているので、TomcatServletWebServerFactoryを確認してください。
@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);
 tomcat.getService().addConnector(connector);
 customizeConnector(connector);
 tomcat.setConnector(connector);
 tomcat.getHost().setAutoDeploy(false);
 configureEngine(tomcat.getEngine());
 for (Connector additionalConnector : this.additionalTomcatConnectors) {
 tomcat.getService().addConnector(additionalConnector);
 }
 prepareContext(tomcat.getHost(), initializers);
 return getTomcatWebServer(tomcat);
}
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
 return new TomcatWebServer(tomcat, getPort() >= 0);
}
//Tomcat.java
//リターンエンジンコンテナは、ここを参照してください、あなたがtomcatのソースコードに精通している場合、エンジンは奇妙に感じることはないだろう。
public Engine getEngine() {
 Service service = getServer().findServices()[0];
 if (service.getContainer() != null) {
 return service.getContainer();
 }
 Engine engine = new StandardEngine();
 engine.setName( "Tomcat" );
 engine.setDefaultHost(hostname);
 engine.setRealm(createDefaultRealm());
 service.setContainer(engine);
 return engine;
}
//Engineは最上位のコンテナで、HostはEngineのサブ・コンテナ、ContextはHostのサブ・コンテナ、WrapperはContextのサブ・コンテナである。

getWebServerメソッドは、TomcatWebServerを返します。

//TomcatWebServer.java
//ここでは、コンストラクタを呼び出してTomcatWebServer
public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
 Assert.notNull(tomcat, "Tomcat Server must not be null");
 this.tomcat = tomcat;
 this.autoStart = autoStart;
 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())) {
 removeServiceConnectors();
 }
 });
 //===tomcatサービスの起動===
 this.tomcat.start();
 rethrowDeferredStartupExceptions();
 try {
 ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
 }
 catch (NamingException ex) {
 }
 //非デーモンプロセスをブロックする
 startDaemonAwaitThread();
 }
 catch (Exception ex) {
 stopSilently();
 destroySilently();
 throw new WebServerException("Unable to start embedded Tomcat", ex);
 }
 }
}
//Tomcat.java
public void start() throws LifecycleException {
 getServer();
 server.start();
}
// server.startTomcatWebServerの
public void stop() throws LifecycleException {
 getServer();
 server.stop();
}
//TomcatWebServer.java
//tomcatサービスの起動
@Override
public void start() throws WebServerException {
 synchronized (this.monitor) {
 if (this.started) {
 return;
 }
 try {
 addPreviouslyRemovedConnectors();
 Connector connector = this.tomcat.getConnector();
 if (connector != null && this.autoStart) {
 performDeferredLoadOnStartup();
 }
 checkThatConnectorsHaveStarted();
 this.started = true;
 //コンテキストがymlで設定されている場合は、このログがコンソールに出力される。
 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());
 }
 }
}
//tomcat サービスを閉じる。
@Override
public void stop() throws WebServerException {
 synchronized (this.monitor) {
 boolean wasStarted = this.started;
 try {
 this.started = false;
 try {
 stopTomcat();
 this.tomcat.destroy();
 }
 catch (LifecycleException ex) {
 }
 }
 catch (Exception ex) {
 throw new WebServerException("Unable to stop embedded Tomcat", ex);
 }
 finally {
 if (wasStarted) {
 containerCounter.decrementAndGet();
 }
 }
 }
}

添付ファイル: tomcatトップレベル構造図

上の図から、サービスには主に複数のコネクタとコンテナが含まれていることがわかります。コネクタは接続関連の処理に使用され、ソケットからリクエストとレスポンスに関連する変換を行います。レスポンス関連の変換。

コンテナは、サーブレットをカプセル化して管理したり、 特定のリクエストを処理したりするために使われます。では、上記の「Engine」→「Host」→「Context」→「Wrapper」コンテナで、 何が行われているのでしょうか?次の図を見てください:

要約すると、tomcatにはサーバーが1つしかありませんが、サーバーには複数のサービスを含めることができ、サービスにはコンテナが1つしかありませんが、複数のコネクタがあるため、1つのサービスで複数の接続を処理できます。

サービスの形成に複数のコネクタとコンテナは、サービスは、外部へのサービスを提供することができますが、サービスはサービスを提供するために、ホスト環境を提供する必要があります、それはサーバーではないので、全体のtomcatステートメントサイクルは、サーバーによって制御されます。

概要

SpringBootの起動は、主に起動するSpringApplicationのインスタンスを介して、起動プロセスは、主に次のことを行います:

プロパティの設定、リスナーの取得、アプリケーション開始イベントの最初の発行、入力パラメータの初期化、環境の設定、バナーの出力、コンテキストの作成、コンテキストの前処理、コンテキストのリフレッシュ、コンテキストの再リフレッシュ、アプリケーション開始イベントの発行、アプリケーション開始完了イベントの発行。

SpringBootの起動では、このステップを上下にリフレッシュしてtomcatが動作します。tomcatの起動は、主に2つのコンポーネントをインスタンス化することです:コネクタ、コンテナ、tomcatインスタンスはサーバーであり、サーバーには複数のサービス、つまり複数のアプリケーションが含まれ、各サービスには複数のコネクタとコンテナが含まれ、コンテナには複数のサブコンテナが含まれます。コンテナには複数のサブコンテナが含まれます。

Read next

プロトタイプとプロトタイプ・チェイニング

JS では、関数のデータ型が定義されるたびに、関数のプロトタイプオブジェクトを指す prototype 属性が生まれ、この属性はオブジェクトのデータ型の値になります。 プロトタイプ・オブジェクトは、同じクラスのすべてのインスタンスがアクセスできるパブリック領域に相当し、オブジェクトに共通する内部的な目的に使用できます...

Nov 14, 2020 · 2 min read