Javaはサーバー側でHTTPリクエストを受け、業務ルールを適用してデータベースへ書き込むバックエンドの言語です。本記事では、Javaがサーバー側を、TypeScriptがブラウザで動く画面側を担当する構成を扱います。本記事では2026年10月時点の公式配布物で構成を組み、Spring Boot 4.1.1のアプリケーションを実際にビルド・起動して確かめた結果をもとに、Javaを選ぶ理由、他言語との違い、フロントエンドとの境界の引き方を整理します。
まとめ:Javaバックエンドで先に決める6項目
| 決めること | 2026年10月時点の結論 | 根拠 |
|---|---|---|
| フレームワーク | Spring Boot 4.1.1 | Initializrの既定値 |
| Javaの版 | 25(LTS)か21 | Boot 4.1.1の互換は17〜26 |
| 同時接続の処理 | I/O待ちが多い場合に仮想スレッドを検討 | Java 21以上で1行 |
| 起動時間の対策 | AOTキャッシュ | 実測で13秒→5秒 |
| 画面側の言語 | TypeScript | 生成したAPI型をコンパイル時に検査 |
| APIの型の共有 | OpenAPIから生成 | 手書きは二重定義になる |
2行目の「25か21」は、Initializrが選択肢に出すJava 27をあえて外した結論です。理由はビルド設定の章で示します。
Javaがバックエンドで選ばれる理由と他言語との比較
利用率の実数:Stack Overflow 2026でJavaは27.6%
Stack Overflow Developer Survey 2026の言語利用率(当該設問の回答者14,099人・複数回答)では、JavaScript 62.0%、Python 58.0%、TypeScript 43.8%に対し、Javaは27.6%で、C#の27.1%とほぼ並びます。Goは16.5%です。
これはフロントエンドやデータ分析も含めた全用途の利用率で、調査は用途別の内訳を出していません。JavaScriptやPythonの高さをそのままサーバー側の採用率と読むことはできない点に注意してください。
Python・Go・TypeScript・C#との違い:型・実行形態・並行処理
バックエンドの言語は、人気よりも型の検査時点・実行形態・同時接続の捌き方で並べると違いが見えます。
| 言語 | 利用率(2026) | 型の検査 | 実行形態 | 同時接続の主な手段 | 代表的なWebフレームワーク |
|---|---|---|---|---|---|
| Java | 27.6% | コンパイル時 | JVM | 仮想スレッド | Spring Boot |
| Python | 58.0% | 実行時(型ヒントは任意) | インタプリタ | asyncio・複数プロセス | Django・FastAPI |
| TypeScript | 43.8% | コンパイル時 | JSへ変換しNode.js等 | イベントループ | Express・NestJS |
| C# | 27.1% | コンパイル時 | .NETランタイム | async/await | ASP.NET Core |
| Go | 16.5% | コンパイル時 | ネイティブバイナリ | goroutine | 標準のnet/http |
Javaが強いのは、型の検査がコンパイル時に済み、トランザクション管理・永続化・認証といった業務システムの部品がSpringの中で一式揃っている点です。逆に、機械学習のライブラリを直接呼ぶAPIならPythonのほうが近道です。どちらを選ぶかの条件は後半の判断基準の章で整理します。Node.js側の選択肢はExpress.jsとは?Express 5対応のバックエンド入門と4系からの移行点が対になります。
弱点の起動時間:AOTキャッシュで13秒が5秒になった実測
Spring Bootのアプリケーションでは、起動時間が運用上の課題になることがあります。所要時間には、JVMのクラス読み込みに加えてBeanの初期化やデータベース接続が効いてきます。コンテナを頻繁に作り直す構成では、この時間がそのまま待ち時間になります。
Spring Boot 4.1.1の公式ドキュメントは、Java 25以上で使えるJVMのAOTキャッシュを、起動時間とメモリ使用量を減らす機能として推奨しています(Java 24以前は前身のCDSを使います)。手順は、jarを展開し、1回だけ学習用の起動をしてキャッシュファイルを作り、本番の起動でそれを指定するだけです。
java -Djarmode=tools -jar backend-0.0.1-SNAPSHOT.jar extract --destination application
cd application
java -XX:AOTCacheOutput=app.aot -Dspring.context.exit=onRefresh -jar backend-0.0.1-SNAPSHOT.jar
java -XX:AOTCache=app.aot -jar backend-0.0.1-SNAPSHOT.jar
この手順を、後述の構成(Web・JPA・H2・springdoc)のアプリケーションに当てて、Temurin 25.0.4.1で3回ずつ起動しました。ログの「Started BackendApplication in」の値は、キャッシュなしで13.046秒・13.102秒・12.689秒、キャッシュありで4.996秒・5.124秒・4.743秒です。検証機はIntel Core i9-9880Hで他の処理も動いていたため、秒数そのものは環境次第ですが、同じ条件で約2.6分の1になりました。
注意点は、公式ドキュメントが「アプリケーションを更新せず同じJavaの版を使う限り」再利用できると条件を付けていることです。jarかJavaの版を更新したらキャッシュも作り直す必要があるので、jarのビルドとキャッシュの生成を同じCIの工程に並べておくと、組み合わせのずれを防げます。
Javaはフロントエンドに使えるか:JDK 26でのApplet API削除
今のブラウザには、Javaを実行する仕組みが標準では入っていません。かつてブラウザのJavaプラグインで使われたApplet APIは、JEP 504によってJDK 26で削除されました(ステータスはClosed/Delivered、Releaseは26)。OracleのAPIドキュメントでも、Applet クラスのページはJava 25には残り、Java 26の同じ位置のURLは版のトップページへ転送されます。既存のJavaアプリケーションやAppletをどうしてもブラウザで動かしたい場合は、CheerpJのように、WebAssemblyとJavaScriptで実装したJVMをページに読み込ませる追加のランタイムを使うことになります。
それでもJavaを画面づくりに使う代表的な方法は2つあります。1つはThymeleafなどのテンプレートエンジンで、サーバー側でHTMLを組み立てて返す構成です。もう1つは、JavaのバイトコードをJavaScriptやWebAssemblyへ変換するコンパイラです。TeaVMは自らを「Javaバイトコード向けのAOTコンパイラで、ブラウザで動くJavaScriptとWebAssemblyを出力する」と説明しています。どちらも成り立ちますが、本記事では、ReactやVueの部品をそのまま使え、APIの生成型をコンパイル時に検査できる点を優先して、画面側をTypeScriptで書く構成を取ります。
層ごとの担当範囲は3層アーキテクチャとインテグレーション層とは|各層の役割とフロントエンド・バックエンドの担当領域で整理しています。なお、よく混同される「バックエンド」と「API」は同じものではありません。APIはバックエンドが外部へ公開する窓口の取り決めで、バックエンドはその裏で検証・業務ルール・永続化を実行する処理全体を指します。
TypeScriptとSpring Bootの役割分担:比較ではなく境界の設計
「TypeScript vs Spring Boot」が成立しない理由
「TypeScriptとSpring Bootのどちらを選ぶか」という問いは、そのままでは答えが出ません。TypeScriptは型注釈を足したJavaScriptという言語で、Spring BootはJVM上で動くフレームワークです。粒度の違うものを並べています。TypeScriptはNode.js上でサーバー側にも使えるので、バックエンドの選定として比べるなら「Node.js+TypeScript(ExpressやNestJS)」と「JVM+Java(Spring Boot)」になります。「Material UIとSpring Bootのどちらか」も同じ取り違えで、前者はReact向けのUI部品集です。
つまり実務で選んでいるのは、サーバー側のランタイムをJVMにするかNode.jsにするかの一点です。JVMを選んでも、ブラウザ側は依然としてTypeScriptが担当するので、両者は同じアプリケーションの別の場所に同時に存在します。
境界はREST API:OpenAPIスキーマからTypeScript型を生成する手順
本記事の構成では、JavaとTypeScriptはREST APIで接続します(GraphQLやWebSocketを使う構成もあります)。ここで避けたいのが、同じデータ構造をJavaの record とTypeScriptの interface に二重で書くことです。片方だけ直してもコンパイルは両方通り、実行時に undefined が出ます。二重定義そのものは、スキーマから型を生成すれば無くせます。
Spring Boot 4.1.1に springdoc-openapi 3.1.1(springdoc-openapi-starter-webmvc-api)を入れ、応答の型を次のように書きました。@NotNull を付けたのは id と name だけです。
public record UserResponse(@NotNull Long id, @NotNull String name, String email) {
static UserResponse from(User u) {
return new UserResponse(u.getId(), u.getName(), u.getEmail());
}
}
起動して /v3/api-docs を取得すると、OpenAPI 3.1.0のスキーマが返り、@NotNull の2項目だけが required に入っていました。
"UserResponse": {
"type": "object",
"properties": {
"id": { "type": "integer", "format": "int64" },
"name": { "type": "string" },
"email": { "type": "string" }
},
"required": ["id", "name"]
}
これを openapi-typescript 7.13.0 で変換すると、required に無い email だけが省略可能な型になります(生成ファイルの components.schemas 部分の抜粋です)。
export interface components {
schemas: {
UserResponse: {
/** Format: int64 */
id: number;
name: string;
email?: string;
};
};
}
この型を使って email を無条件に参照するコードを書き、npmの最新であるTypeScript 7.0.2の tsc --strict にかけると、コンパイルの段階で止まります。
import type { components } from "./api";
type User = components["schemas"]["UserResponse"];
function label(u: User): string {
return u.email.toLowerCase();
}
use.ts(6,10): error TS18048: 'u.email' is possibly 'undefined'.
Java側で必須にしなかった項目が、フロントエンドのコンパイルエラーとして表に出ています。手書きの interface では、書いた人が省略を想定していなければ必須の型を書いてしまい、この検出は起きません。APIの型はJava側のスキーマを唯一の正とし、TypeScript側は生成物だけを参照する運用にしてください。生成はCIでも走らせ、生成物に差分が出たらビルドを失敗させるのが確実です。
ただし、型の生成はスキーマと実際の応答が一致していることまでは保証しません。この検証アプリは、email が空のユーザーに対して {"id":1,"name":"Sato","email":null} を返しました。生成された型は email?: string(省略される可能性はあるが、あれば文字列)なので、null はこの型に収まりません。省略可能と null 許容は別物です。この構成では spring.jackson.default-property-inclusion=non_null を設定すると、応答が {"id":1,"name":"Sato"} になり、スキーマと揃いました。逆に null を返す設計にするなら、スキーマ側で null を許す型として出力させる必要があります。どちらを選んでも、実際の応答をスキーマで検証するテストを1本置いておくと、こうしたずれをCIで止められます。
1つ見落としやすい点があります。起動ログには「SpringDoc /v3/api-docs endpoint is enabled by default.」という警告が出ます。スキーマの公開は既定で有効なので、本番環境では springdoc.api-docs.enabled=false で閉じるか、認証の内側に置いてください。版の組み合わせと導入手順はSpringdoc-openapiとは?Spring Boot 3/4での導入方法とバージョン選定を解説【2026年最新】、CORSやJWT認証まで含めたReactとの接続はSpring BootとReactの連携方法|REST API・CORS・JWT認証から1つのjarへの同梱まで【Spring Boot 4.1】で扱っています。
Spring Boot 4.1で組むバックエンドの層構成
コントローラ・サービス・リポジトリの分け方
Spring Bootのバックエンドは、HTTPを扱うコントローラ、業務ルールとトランザクションを持つサービス、永続化を担うリポジトリの3層に分けるのが基本です。コントローラはリクエストの受け取りと応答の組み立てだけを担当させます。
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/{id}")
public UserResponse get(@PathVariable Long id) {
return UserResponse.from(userService.get(id));
}
}
サービスはコンストラクタでリポジトリを受け取ります。単一コンストラクタなら @Autowired は不要で、テストではコンストラクタにモックを渡して依存を差し替えられます。見つからないときは ResponseStatusException で404を返します。
@Service
public class UserService {
private final UserRepository repository;
public UserService(UserRepository repository) {
this.repository = repository;
}
@Transactional(readOnly = true)
public User get(Long id) {
return repository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
}
}
リポジトリはインターフェースを宣言するだけで、基本の検索と保存が使えます。
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
}
この3ファイルに、前掲の UserResponse、User エンティティ、H2の依存と1行の初期データ(data.sql)を追加し、Hibernateのテーブル作成後に初期データを投入するため、application.properties に spring.jpa.defer-datasource-initialization=true を設定して起動すると、/api/users/1 はユーザーを返し、存在しないIDは404を返しました。1つのテーブルで完結する問い合わせはリポジトリのメソッド名で表し、複数の集約をまたぐ処理や「更新して通知も送る」のような副作用を伴う処理はサービスに置きます。各アノテーションの役割はSpring Bootのアノテーション一覧|役割別の早見表とBoot 4での変更点にまとめています。
トランザクション境界と起動時に出るOSIVの警告
トランザクション境界はサービスのメソッドに置き、コントローラには置きません。ただしJPAを入れたSpring Bootには、それとは別にデータベースとのセッションを延ばす既定値があります。起動すると「spring.jpa.open-in-view is enabled by default.」で始まる警告が出ます。これはリクエストの最後までJPAのセッションを開いたままにする設定(OSIV)が既定で有効だという通知で、コントローラや応答の組み立て中にも遅延読み込みのクエリが走り得ます。
新規のバックエンドなら spring.jpa.open-in-view=false を明示し、必要なデータはサービスの中で読み切って応答用の型へ変換しておきます。こうすると、応答の組み立て中に遅延読み込みのクエリが走ることはなくなります。既存システムで切り替える際の判断はspring.jpa.open-in-viewとは?OSIVの既定値trueと起動時警告の消し方・falseの判断基準で解説しています。
仮想スレッドの有効化:Java 21以上で1行
データベースや外部APIの応答待ちが長いAPIで同時接続が多いなら、仮想スレッドを有効にします。Spring Bootの公式リファレンスは、仮想スレッドにはJava 21以上が必要で、spring.threads.virtual.enabled を true にすれば有効になると説明しています。同じページは、Java 24以上を強く推奨し、仮想スレッドがプラットフォームスレッドに固定される「ピン留め」でスループットが下がる場合があるとも書いています。Java 21を選ぶなら、有効化の前後で負荷試験を比べてください。この機能はSpring Boot 3.2で入りました。
spring.threads.virtual.enabled=true
設定後にリクエスト処理中のスレッド名を返すエンドポイントを叩くと、VirtualThread[#60,tomcat-handler-0]/runnable@ForkJoinPool-1-worker-1 が返り、Tomcatのリクエストが仮想スレッドで処理されていることを確認できました。データベース待ちのようにI/Oで止まる時間が長い処理ほど効果が出ます。CPUを使い切る計算処理は速くならないので、その種類の負荷なら効果は見込めません。
ビルド設定とJavaの版:Initializr生成物の落とし穴
toolchain指定のJDK不在によるビルド失敗と対処
2026年10月12日時点の Spring Initializr で、Spring Boot 4.1.1・Java 21・Web/JPA/Validationを指定して生成した build.gradle の要部がこちらです。
plugins {
id 'java'
id 'org.springframework.boot' version '4.1.1'
id 'io.spring.dependency-management' version '1.1.7'
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-webmvc'
testImplementation 'org.springframework.boot:spring-boot-starter-data-jpa-test'
testImplementation 'org.springframework.boot:spring-boot-starter-validation-test'
testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
Javaの版は toolchain で指定されます。これはビルドを実行するJDKとコンパイルに使うJDKを分ける仕組みで、指定した版のJDKを手元から探します。JDK 25とJDK 26しか入っていない環境でこのままビルドすると、次のエラーで止まりました。
Cannot find a Java installation on your machine (Mac OS X 26.6.2 x86_64) matching: {languageVersion=21, vendor=any vendor, implementation=vendor-specific, nativeImageCapable=false}. Toolchain download repositories have not been configured.
Initializrが生成する settings.gradle は rootProject.name の1行だけで、JDKを自動で取得する設定が入っていないためです。対処は2つあります。指定した版のJDKを入れるか、Gradleのtoolchain文書にある自動取得のプラグインを settings.gradle に足すかです。
plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0'
}
rootProject.name = 'backend'
チームで使うなら、プラグインを足すほうが「自分の環境では通る」問題を潰せます。ただし社内ネットワークから外部のJDK配布元へ出られない環境では使えないので、その場合はJDKの配布手順を別に用意してください。
Javaの版の選び方:Initializrは27を出すが互換範囲は26まで
Initializrが選択肢に出すJavaの版は27・25・21・17の4つで、既定は17です。一方、Spring Boot 4.1.1のSystem Requirementsは「requires at least Java 17 and is compatible with versions up to and including Java 26」と書いており、互換範囲にJava 27は入っていません。JDK 27は2026年9月15日に出たばかりの非LTS版で、Oracleのロードマップ上、最新のLTSは25、次のLTSは2027年9月予定の29です。
新規のバックエンドなら25を選んでください。互換範囲の内側にあり、前述のAOTキャッシュもJava 25以上が条件です。21でも動きますが、AOTキャッシュの代わりに旧方式のCDSを使うことになります。Java 27を入れる判断はJava 27とは|非LTSの半年運用とG1・オブジェクトヘッダ既定化の影響で扱っています。
Webスターターの名称変更とGradleラッパーの更新
生成物のWeb依存は spring-boot-starter-webmvc です。Maven Centralの spring-boot-starter-web 4.1.1 のPOMは、説明文に「deprecated in favor of spring-boot-starter-webmvc」と書いています。4系では生成物の名前に合わせてください。スターター名の変更の全体はSpring Bootとは|4.1系の依存名の変更と最小Web APIの作り方にまとめています。
もう1点、生成されたラッパーは gradle-9.7.1-bin.zip を指していますが、Gradle本体の現行版は9.8.1(2026年10月7日ビルド)です。Spring Boot 4.1.1が対応を明記しているのはGradle 8.14以降と9.x なので、どちらでも動きます。上げるときは ./gradlew wrapper --gradle-version 9.8.1 で上げてコミットします。Spring Boot自体の版は、公式のサポート表で4.1.xのOSSサポートが2027年7月31日までなので、その前に次の系列へ上げる計画を立ててください。系列ごとの期限はSpring Bootバージョン一覧とサポート期限【2026年8月】最新4.1と3.5 EOL後の選び方にあります。
Javaバックエンドを選ばない判断基準
Javaが常に正解ではありません。次の条件に当てはまるなら、先に別の言語を候補にして工数を見積もってください。
- チームにJVMの運用経験者がおらず、フロントエンド出身者だけで構成されている場合。サーバー側もTypeScriptに揃えたほうが、採用とレビューの負担が軽くなります。
- 関数単位で起動と終了を繰り返すサーバーレス構成が主役で、デプロイの頻度も高い場合。AOTキャッシュはjarとJavaの版が変わるたびに作り直しが要るため、運用の手間が先に立ちます。
- APIが数本で業務ルールもほとんど無く、社内にSpringの知見も既存のJava基盤も無い場合。依存とJVMの運用を新たに抱えることになり、得られるものより負担が先に立ちます。
逆に、複数テーブルへの更新を1つのトランザクションで束ねる要件があり、バッチ処理と業務ロジックを長期間保守し、社内にJavaとSpringの運用経験があるなら、Javaを第一候補にしてください。この判断は言語の速度比較ではなく、トランザクション要件・保守期間・チームの経験の3つで決まります。Javaでの業務システムをゼロから設計・開発する場合は、フルスクラッチ開発として要件定義から相談を受けています。
よくある質問
Javaはバックエンド言語ですか?
はい。JavaはJVM上で動くサーバー側の言語として使われ、Spring Bootなどのフレームワークと組み合わせてHTTPリクエストの処理、業務ルール、データベースへのアクセスを担当します。名前の似たJavaScriptは仕様も実行環境も別の言語です。
Javaでフロントエンドは書けますか?
ブラウザにはJavaを実行する仕組みが標準では入っておらず、かつてのJavaプラグインで使われたApplet APIは、JEP 504によりJDK 26で削除されました。Thymeleafのようにサーバー側でHTMLを組み立てる構成、TeaVMのようにJavaのバイトコードをJavaScriptへ変換する方法、CheerpJのような追加ランタイムはありますが、画面に状態を持つアプリケーションならTypeScriptで書くのが一般的です。
バックエンドにはJavaとPythonのどちらが向いていますか?
処理の中身で分かれます。機械学習やデータ処理のライブラリを直接呼ぶAPIならPython、複数テーブルの更新を1トランザクションで束ねる業務システムを長く保守するならJavaが向きます。Stack Overflow 2026の利用率はPython 58.0%、Java 27.6%ですが、これは全用途の数字で、バックエンドでの優劣を示すものではありません。
Spring BootとTypeScriptはどちらを選ぶべきですか?
択一ではありません。Spring Bootはサーバーのフレームワークで、TypeScriptは画面側とNode.js上のサーバー側の両方で使う言語なので、Spring Bootのサーバーと TypeScriptの画面を同じアプリケーションで併用できます。選ぶ対象はサーバーのランタイムをJVMにするかNode.jsにするかで、JVMを選んでも画面側はTypeScriptのままです。
Spring Boot 4.1にはJavaのどのバージョンが必要ですか?
Spring Boot 4.1.1はJava 17以上が必須で、互換を明記している上限はJava 26です。Initializrの選択肢にはJava 27もありますが互換範囲の外なので、新規開発では最新LTSのJava 25を選ぶのが確実です。