サンプルコードで身につける: SpringApplication.run がやること
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
1start.spring.io が生成する main クラス
で作ったプロジェクトに最初から入っているの全文です。自分のコードと呼べるのは run の1行だけで、ポート番号もサーバの種類もここには出てきません。それでもこのクラスを起動すると が 8080 番で待ち受けます。
package com.example.web;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class WebApplication {
public static void main(String[] args) {
SpringApplication.run(WebApplication.class, args);
}
}
2起動ログを上から読む
上の クラスを起動したときのログ全文です( 4.1.1 / 25)。Tomcat initialized の行でポートが決まり、Started の行で run の主な仕事が終わっています。起動中の行は欄がどれも main で、起動の処理が の上で進んでいることが読めます。最後の2行は実験でを止めたときの終了処理です。
. ____ _ __ _ _/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/ ___)| |_)| | | | | || (_| | ) ) ) )' |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot :: (v4.1.1)2026-10-01T13:18:59.532Z INFO 1 --- [web] [ main] com.example.web.WebApplication : Starting WebApplication using Java 25.0.4.1 with PID 1 (/work/web/target/classes started by root in /work/web)2026-10-01T13:18:59.534Z INFO 1 --- [web] [ main] com.example.web.WebApplication : No active profile set, falling back to 1 default profile: "default"2026-10-01T13:19:00.008Z INFO 1 --- [web] [ main] o.s.boot.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http)2026-10-01T13:19:00.017Z INFO 1 --- [web] [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]2026-10-01T13:19:00.017Z INFO 1 --- [web] [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/11.0.24]2026-10-01T13:19:00.035Z INFO 1 --- [web] [ main] b.w.c.s.WebApplicationContextInitializer : Root WebApplicationContext: initialization completed in 452 ms2026-10-01T13:19:00.244Z INFO 1 --- [web] [ main] o.s.boot.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/'2026-10-01T13:19:00.247Z INFO 1 --- [web] [ main] com.example.web.WebApplication : Started WebApplication in 0.947 seconds (process running for 1.105)2026-10-01T13:19:00.379Z INFO 1 --- [web] [ionShutdownHook] o.s.boot.tomcat.GracefulShutdown : Commencing graceful shutdown. Waiting for active requests to complete2026-10-01T13:19:00.381Z INFO 1 --- [web] [tomcat-shutdown] o.s.boot.tomcat.GracefulShutdown : Graceful shutdown complete
3起動秒数を5回測る
同じ main クラスを、毎回新しいコンテナで5回起動した値です。条件は、依存が だけ・自作の なし・展開済みのクラスと 群から起動・ オプションなし・ファイルがキャッシュに載った状態・コンテナから見える CPU は 10 個です。左の列と右の列は別の数で、右は JVM の起動からの経過です。この条件での範囲であり、Spring Boot 一般の起動時間ではありません。
回 | Started ... in(秒) | process running for---+----------------------+--------------------1 | 0.947 | 1.1052 | 0.865 | 1.0733 | 0.734 | 0.94 | 0.732 | 0.8965 | 0.718 | 0.871
4JAR を外すと Web アプリでなくなる
イベントを印字する同じクラスを、から Tomcat と まわりの JAR を外して起動した出力です。コードは変えていないのに種別は NONE になり、Tomcat の行が消え、のクラスも別のものになりました。下のコードは種別を決めている Spring Boot 4.1.1 の WebApplicationType の抜粋で、クラスがあるかどうかを調べています。4.1.1 では spring-boot-webmvc の JAR も判定役(Deducer)を登録しています。
>> webApplicationType = NONE>> event: ApplicationStartingEvent>> event: ApplicationEnvironmentPreparedEvent>> event: ApplicationContextInitializedEvent[log] Starting EventsApp using Java 25.0.4.1 with PID 8 (/work/web/target/classes started by root in /work/web)[log] No active profile set, falling back to 1 default profile: "default">> event: ApplicationPreparedEvent>> event: ContextRefreshedEvent[log] Started EventsApp in 0.474 seconds (process running for 0.608)>> event: ApplicationStartedEvent>> event: AvailabilityChangeEvent>> CommandLineRunner.run args=[]>> ApplicationRunner.run nonOptionArgs=[]>> event: ApplicationReadyEvent>> event: AvailabilityChangeEvent>> run() returned: AnnotationConfigApplicationContext>> event: ContextClosedEvent
// 出所: spring-boot 4.1.1 / org.springframework.boot.WebApplicationType(抜粋)
private static final String[] SERVLET_INDICATOR_CLASSES = { "jakarta.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
/**
* Deduce the {@link WebApplicationType} from the current classpath.
* @return the deduced web application
* @since 4.0.1
*/
public static WebApplicationType deduce() {
for (Deducer deducer : SpringFactoriesLoader.forDefaultResourceLocation().load(Deducer.class)) {
WebApplicationType deduced = deducer.deduceWebApplicationType();
if (deduced != null) {
return deduced;
}
}
return isServletApplication() ? WebApplicationType.SERVLET : WebApplicationType.NONE;
}
private static boolean isServletApplication() {
for (String servletIndicatorClass : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(servletIndicatorClass, null)) {
return false;
}
}
return true;
}
5NestJS の main.ts と並べる
NestJS では、コンテナを組み立てる create と、ポートを開く listen を自分で別々に呼びます。listen を書かなければサーバは立ちません。Spring Boot では run の1行で、クラスパスから Web アプリかどうかが決まり、必要なら Tomcat の起動まで進みます。書く量の差ではなく、ポートを開く判断をどこに置いたかの違いです。
// NestJS: main.ts
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
async function bootstrap() {
const app = await NestFactory.create(AppModule); // コンテナを組み立てる
await app.listen(3000); // ポートを開くのは自分で呼ぶ
}
bootstrap();
// Spring Boot: 対応する部分は1行
// SpringApplication.run(WebApplication.class, args);
// ポート番号は設定値(既定は 8080)、サーバを起動するかどうかはクラスパスで決まる