🆕 2025年11月最新版!
Spring Boot 3.4、Java 21対応、実践的なトラブルシューティング、Docker統合を反映しました。
5分でわかる:Spring Boot完全ロードマップ
Javaで効率的にWebアプリを開発したい。そんなあなたのために、Spring Bootの全てを実践的に解説します。
なぜこのガイドが必要なのか
| 課題 | よくある失敗 | このガイドの解決策 |
|---|---|---|
| 環境構築で挫折 | 設定エラーで動かない | ステップバイステップ手順 |
| 概念理解できない | DI、AOPが不明 | 図解と実例で理解 |
| エラーが解決できない | つまづいて進めない | エラー15選と解決策 |
| 実務で使えない | 基本しか知らない | 実践的なアプリ開発 |
本記事で学べること
- 基礎理解(第1-2章):Spring Bootの特徴、環境構築
- 核心概念(第3章):依存性注入(DI)の完全理解
- 実践開発(第4-5章):REST API、データベース連携
- トラブル解決(第6章):よくあるエラー15選
- 実務スキル(第7-8章):実践アプリ、デプロイ
最短で課題解決する一冊
この記事の内容と高い親和性が確認できたベストマッチです。早めにチェックしておきましょう。
第1章:Spring Boot完全解説
1.1 Spring Bootとは?
Spring Boot:
├── Javaベースのアプリケーションフレームワーク
├── Spring Frameworkをベースに開発を簡素化
├── 「設定より規約」でボイラープレート削減
└── エンタープライズ向け機能を標準搭載
開発元:
- 運営:Spring(VMware Tanzu)
- 初版リリース:2014年4月
- 最新版:Spring Boot 3.4(2024年11月)
- 採用企業:Netflix、Alibaba、楽天等
主な特徴:
✅ 自動設定(Auto-configuration)
✅ スタンドアロン実行可能
✅ 組み込みサーバー(Tomcat、Jetty等)
✅ プロダクション対応機能標準搭載1.2 Spring Bootができること
主要機能:
1. Web アプリケーション開発
└─ RESTful API、マイクロサービス
├── Spring MVC(従来型)
├── Spring WebFlux(リアクティブ)
└── WebSocket対応
2. データアクセス
└─ JPA、JDBC、NoSQL対応
├── Spring Data JPA
├── Spring Data MongoDB
├── Spring Data Redis
└── MyBatis統合
3. セキュリティ
└─ Spring Security統合
├── 認証・認可
├── OAuth2/OpenID Connect
└── CSRF対策
4. クラウドネイティブ対応
└─ マイクロサービス基盤
├── Spring Cloud連携
├── サービスディスカバリ
└── 分散トレーシング
5. 運用・監視
└─ Spring Boot Actuator
├── ヘルスチェック
├── メトリクス収集
└── ログ管理1.3 従来のSpringとの違い
| 項目 | 従来のSpring | Spring Boot |
|---|---|---|
| 設定 | XML設定大量 | アノテーション中心 |
| サーバー | 外部Tomcat必須 | 組み込みサーバー |
| 起動時間 | 遅い | 高速 |
| 学習コスト | 高い | 低い |
| ボイラープレート | 多い | 最小限 |
コード比較:
// 従来のSpring(簡略版でも冗長)
@Configuration
@EnableWebMvc
@ComponentScan
public class WebConfig extends WebMvcConfigurerAdapter {
// 大量の設定コード...
}
// Spring Boot(シンプル)
@SpringBootApplication
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}さらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
第2章:開発環境構築
2.1 必要な環境
必須環境:
□ JDK 17以上(Spring Boot 3.x)
└─ 推奨:Java 21(LTS、Virtual Threads対応)
□ ビルドツール
├─ Maven 3.6以上
└─ または Gradle 7.6以上
□ IDE(推奨順)
1. IntelliJ IDEA(Community/Ultimate)
2. Spring Tool Suite(STS)
3. VS Code + Spring Boot Extension Pack
推奨スペック:
□ メモリ:8GB以上
□ ストレージ:空き容量20GB以上
□ OS:Windows、macOS、Linux2.2 JDKのインストール
# macOS (Homebrew)
brew install openjdk@21
# Ubuntu/Debian
sudo apt install openjdk-21-jdk
# Windows
# https://adoptium.net/ からインストーラーダウンロード
# バージョン確認
java -version
# openjdk version "21.0.1" 2023-10-17 LTS2.3 IDEのセットアップ
IntelliJ IDEA(推奨):
1. https://www.jetbrains.com/idea/download/
2. Community Edition(無料)で十分
3. 初回起動時にSpringプラグイン推奨
Spring Tool Suite:
1. https://spring.io/tools
2. Eclipse ベース、Spring専用
3. Spring Initializr統合
VS Code:
1. Extension Pack for Java インストール
2. Spring Boot Extension Pack インストール
3. 軽量だが機能は限定的2.4 Spring Initializrでプロジェクト作成
方法1:Webブラウザ
1. https://start.spring.io/ にアクセス
2. 設定項目:
- Project: Maven or Gradle
- Language: Java
- Spring Boot: 3.4.x(最新安定版)
- Packaging: Jar
- Java: 21
3. Project Metadata:
- Group: com.example
- Artifact: demo
- Name: demo
- Package name: com.example.demo
4. Dependencies追加:
- Spring Web
- Spring Data JPA
- H2 Database
- Lombok(オプション)
5. 「Generate」クリック → ZIPダウンロード
方法2:IDEから直接作成
IntelliJ IDEA:
File → New → Project → Spring Initializr
VS Code:
Ctrl+Shift+P → Spring Initializr: Create a Maven Project2.5 プロジェクト構造の理解
demo/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/example/demo/
│ │ │ ├── DemoApplication.java ← メインクラス
│ │ │ ├── controller/ ← コントローラー
│ │ │ ├── service/ ← ビジネスロジック
│ │ │ ├── repository/ ← データアクセス
│ │ │ └── model/ ← エンティティ
│ │ └── resources/
│ │ ├── application.properties ← 設定ファイル
│ │ ├── static/ ← 静的リソース
│ │ └── templates/ ← テンプレート
│ └── test/
│ └── java/
│ └── com/example/demo/
├── pom.xml (Mavenの場合)
└── build.gradle (Gradleの場合)
重要ファイル:
- DemoApplication.java: アプリケーションのエントリーポイント
- application.properties: 設定(ポート、DB接続等)
- pom.xml/build.gradle: 依存関係管理さらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
第3章:依存性注入(DI)の完全理解
3.1 DIとは何か
依存性注入(Dependency Injection):
従来の方法(密結合):
class UserService {
private UserRepository repo = new UserRepositoryImpl(); // 直接生成
// repoの実装変更時にUserServiceも変更必要
}
DI適用(疎結合):
@Service
class UserService {
private final UserRepository repo;
// Springが自動的にrepoを注入
public UserService(UserRepository repo) {
this.repo = repo;
}
// repoの実装変更してもUserServiceは変更不要
}
メリット:
✅ テストが容易(モック注入可能)
✅ 保守性向上(疎結合)
✅ 再利用性向上
✅ 設定の一元管理3.2 DIの3つの方法
// 方法1:コンストラクタ注入(推奨)
@Service
public class UserService {
private final UserRepository userRepository;
// @Autowired は単一コンストラクタなら省略可能
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
// 方法2:セッター注入
@Service
public class UserService {
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
// 方法3:フィールド注入(非推奨)
@Service
public class UserService {
@Autowired
private UserRepository userRepository; // テストが困難
}
推奨順位:
1位: コンストラクタ注入(不変性、テスト容易)
2位: セッター注入(オプション依存の場合)
3位: フィールド注入(避けるべき)3.3 Beanのスコープとライフサイクル
// スコープの種類
// 1. singleton(デフォルト)
@Service
public class UserService {
// アプリケーション全体で1つのインスタンス
}
// 2. prototype
@Service
@Scope("prototype")
public class UserService {
// 注入されるたびに新しいインスタンス
}
// 3. request(Webアプリ)
@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestScopedBean {
// HTTPリクエストごとに新しいインスタンス
}
// 4. session(Webアプリ)
@Component
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class SessionScopedBean {
// HTTPセッションごとに新しいインスタンス
}さらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
第4章:REST API開発
4.1 基本的なCRUD API
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor // Lombokでコンストラクタ自動生成
public class UserController {
private final UserService userService;
// 全ユーザー取得
@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
return ResponseEntity.ok(userService.findAll());
}
// ID指定でユーザー取得
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
return ResponseEntity.ok(userService.findById(id));
}
// ユーザー作成
@PostMapping
public ResponseEntity<User> createUser(@Valid @RequestBody UserDto userDto) {
User created = userService.create(userDto);
return ResponseEntity
.created(URI.create("/api/users/" + created.getId()))
.body(created);
}
// ユーザー更新
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(
@PathVariable Long id,
@Valid @RequestBody UserDto userDto) {
return ResponseEntity.ok(userService.update(id, userDto));
}
// ユーザー削除
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.delete(id);
return ResponseEntity.noContent().build();
}
// 検索API
@GetMapping("/search")
public ResponseEntity<List<User>> searchUsers(
@RequestParam(required = false) String name,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
return ResponseEntity.ok(userService.search(name, page, size));
}
}4.2 バリデーション
// DTOクラス
@Data
public class UserDto {
@NotBlank(message = "名前は必須です")
@Size(min = 2, max = 50, message = "名前は2文字以上50文字以下で入力してください")
private String name;
@NotBlank(message = "メールアドレスは必須です")
@Email(message = "有効なメールアドレスを入力してください")
private String email;
@Min(value = 0, message = "年齢は0以上である必要があります")
@Max(value = 150, message = "年齢は150以下である必要があります")
private Integer age;
@Pattern(regexp = "^\\d{3}-\\d{4}-\\d{4}$",
message = "電話番号は000-0000-0000の形式で入力してください")
private String phone;
}
// カスタムバリデーション
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UniqueEmailValidator.class)
public @interface UniqueEmail {
String message() default "このメールアドレスは既に使用されています";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}4.3 グローバルエラーハンドリング
@RestControllerAdvice
public class GlobalExceptionHandler {
// リソース未検出
@ExceptionHandler(ResourceNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorResponse handleResourceNotFound(ResourceNotFoundException ex) {
return ErrorResponse.builder()
.timestamp(LocalDateTime.now())
.status(HttpStatus.NOT_FOUND.value())
.error("Not Found")
.message(ex.getMessage())
.build();
}
// バリデーションエラー
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ValidationErrorResponse handleValidationExceptions(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach(error -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return ValidationErrorResponse.builder()
.timestamp(LocalDateTime.now())
.status(HttpStatus.BAD_REQUEST.value())
.errors(errors)
.build();
}
// 一般的なエラー
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponse handleGenericException(Exception ex) {
return ErrorResponse.builder()
.timestamp(LocalDateTime.now())
.status(HttpStatus.INTERNAL_SERVER_ERROR.value())
.error("Internal Server Error")
.message("予期しないエラーが発生しました")
.build();
}
}さらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
第5章:データベース連携
5.1 エンティティとリポジトリ
// エンティティ
@Entity
@Table(name = "users")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 50)
private String name;
@Column(nullable = false, unique = true, length = 100)
private String email;
private Integer age;
@Column(name = "created_at", updatable = false)
@CreationTimestamp
private LocalDateTime createdAt;
@Column(name = "updated_at")
@UpdateTimestamp
private LocalDateTime updatedAt;
// リレーション例
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Post> posts = new ArrayList<>();
}
// リポジトリ
public interface UserRepository extends JpaRepository<User, Long> {
// メソッド名から自動クエリ生成
Optional<User> findByEmail(String email);
List<User> findByNameContaining(String name);
List<User> findByAgeGreaterThan(Integer age);
List<User> findByAgeB etween(Integer minAge, Integer maxAge);
boolean existsByEmail(String email);
// カスタムクエリ(JPQL)
@Query("SELECT u FROM User u WHERE u.name LIKE %:name% AND u.age >= :age")
List<User> findByNameAndAgeCustom(@Param("name") String name, @Param("age") Integer age);
// ネイティブSQL
@Query(value = "SELECT * FROM users WHERE LOWER(email) LIKE LOWER(CONCAT('%', :query, '%'))",
nativeQuery = true)
List<User> searchByEmail(@Param("query") String query);
// ページング・ソート
Page<User> findByNameContaining(String name, Pageable pageable);
}5.2 サービス層の実装
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class UserService {
private final UserRepository userRepository;
public List<User> findAll() {
return userRepository.findAll();
}
public User findById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("User not found: " + id));
}
@Transactional
public User create(UserDto dto) {
// メールアドレス重複チェック
if (userRepository.existsByEmail(dto.getEmail())) {
throw new DuplicateResourceException("Email already exists: " + dto.getEmail());
}
User user = User.builder()
.name(dto.getName())
.email(dto.getEmail())
.age(dto.getAge())
.build();
return userRepository.save(user);
}
@Transactional
public User update(Long id, UserDto dto) {
User user = findById(id);
user.setName(dto.getName());
user.setEmail(dto.getEmail());
user.setAge(dto.getAge());
return userRepository.save(user);
}
@Transactional
public void delete(Long id) {
User user = findById(id);
userRepository.delete(user);
}
public Page<User> search(String name, int page, int size) {
Pageable pageable = PageRequest.of(page, size, Sort.by("name").ascending());
return userRepository.findByNameContaining(name, pageable);
}
}さらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
第6章:よくあるエラー15選と解決策
カテゴリ1:起動・設定エラー(1-5)
エラー1:起動時のポート競合
エラーメッセージ:
Web server failed to start. Port 8080 was already in use.
原因:
既に8080ポートを使用しているプロセスが存在
解決策:
方法1:ポート番号を変更
```properties
# application.properties
server.port=8081方法2:既存プロセスを終了
# macOS/Linux
lsof -i :8080
kill -9 <PID>
# Windows
netstat -ano | findstr :8080
taskkill /PID <PID> /F方法3:ランダムポート使用
server.port=0
#### エラー2:Bean創造失敗
エラーメッセージ: Parameter 0 of constructor in com.example.UserService required a bean of type 'UserRepository' that could not be found.
原因:
- リポジトリにアノテーション(@Repository)がない
- コンポーネントスキャン対象外
- 循環参照
解決策:
- アノテーション確認
@Repository // これが必要
public interface UserRepository extends JpaRepository<User, Long> {
}- パッケージ構造確認
com.example.demo/ ← @SpringBootApplicationの位置
├── DemoApplication.java
├── controller/
├── service/
└── repository/ ← 同じかサブパッケージに配置- 明示的なBean定義
@Configuration
public class AppConfig {
@Bean
public UserRepository userRepository() {
return new UserRepositoryImpl();
}
}
#### エラー3-5のクイックリファレンス
| エラー | 原因 | 解決策 |
|--------|------|--------|
| **3. DataSource設定エラー** | DB接続情報不足 | application.propertiesに接続情報追加 |
| **4. ClassNotFoundイエラー** | 依存関係不足 | pom.xml/build.gradleに依存追加 |
| **5. Auto設定衝突** | 複数の設定が競合 | @EnableAutoConfiguration(exclude={...}) |
### カテゴリ2:実装エラー(6-10)
#### エラー6:LazyInitializationException
エラーメッセージ: LazyInitializationException: failed to lazily initialize a collection of role
原因: JPAのLazy Loadingで、セッション外でデータアクセス
解決策:
方法1:Eager Loading
@Entity
public class User {
@OneToMany(fetch = FetchType.EAGER) // Lazyの代わり
private List<Post> posts;
}方法2:@Transactional追加
@Transactional(readOnly = true)
public User getUserWithPosts(Long id) {
User user = userRepository.findById(id).orElseThrow();
user.getPosts().size(); // セッション内で初期化
return user;
}
``
方法3:JOIN FETCH
```java
@Query("SELECT u FROM User u LEFT JOIN FETCH u.posts WHERE u.id = :id")
Optional<User> findByIdWithPosts(@Param("id") Long id);
#### エラー7-10のクイックリファレンス
| エラー | 原因 | 解決策 |
|--------|------|--------|
| **7. N+1問題** | クエリ大量発行 | @EntityGraph、JOIN FETCH使用 |
| **8. TransactionRequiredException** | トランザクション不足 | @Transactional追加 |
| **9. ConstraintViolationException** | DB制約違反 | バリデーション強化、制約確認 |
| **10. OptimisticLockException** | 同時更新競合 | @Version追加、楽観的ロック |
### カテゴリ3:実行時エラー(11-15)
#### エラー11:NullPointerException in DI
原因: @Autowiredされていないフィールドへのアクセス
悪い例:
@RestController
public class UserController {
// この時点ではnull
private UserService userService;
@GetMapping("/users")
public List<User> getUsers() {
return userService.findAll(); // NullPointerException!
}
}正しい例:
@RestController
@RequiredArgsConstructor // Lombokでコンストラクタ生成
public class UserController {
private final UserService userService; // final にする
@GetMapping("/users")
public List<User> getUsers() {
return userService.findAll(); // OK
}
}
#### エラー12-15のクイックリファレンス
| エラー | 原因 | 解決策 |
|--------|------|--------|
| **12. OutOfMemoryError** | メモリリーク | JVMヒープ増加、コード見直し |
| **13. 404エラー** | ルーティング間違い | @RequestMapping確認 |
| **14. 405エラー** | HTTPメソッド不一致 | @GetMapping等を正しく使用 |
| **15. CORS エラー** | クロスオリジン制限 | @CrossOrigin追加、CORS設定 |
## 第7章:実践アプリ開発
### 7.1 Todo管理APIの完全実装
```java
// Entity
@Entity
@Table(name = "todos")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Todo {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String title;
private String description;
@Column(nullable = false)
private Boolean completed = false;
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate dueDate;
@CreationTimestamp
private LocalDateTime createdAt;
@UpdateTimestamp
private LocalDateTime updatedAt;
}
// Repository
public interface TodoRepository extends JpaRepository<Todo, Long> {
List<Todo> findByCompletedOrderByDueDateAsc(Boolean completed);
List<Todo> findByDueDateBefore(LocalDate date);
@Query("SELECT t FROM Todo t WHERE t.completed = false AND t.dueDate < :today")
List<Todo> findOverdueTodos(@Param("today") LocalDate today);
}
// Service
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class TodoService {
private final TodoRepository todoRepository;
public List<Todo> findAll() {
return todoRepository.findAll();
}
public List<Todo> findPending() {
return todoRepository.findByCompletedOrderByDueDateAsc(false);
}
public List<Todo> findOverdue() {
return todoRepository.findOverdueTodos(LocalDate.now());
}
@Transactional
public Todo create(TodoDto dto) {
Todo todo = Todo.builder()
.title(dto.getTitle())
.description(dto.getDescription())
.dueDate(dto.getDueDate())
.completed(false)
.build();
return todoRepository.save(todo);
}
@Transactional
public Todo toggleComplete(Long id) {
Todo todo = todoRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Todo not found"));
todo.setCompleted(!todo.getCompleted());
return todoRepository.save(todo);
}
}さらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
第8章:デプロイと運用
8.1 実行可能JARの作成
# Maven
./mvnw clean package
# Gradle
./gradlew bootJar
# 実行
java -jar target/myapp-0.0.1-SNAPSHOT.jar8.2 Docker化
# Dockerfile
FROM eclipse-temurin:21-jdk-alpine AS build
WORKDIR /workspace/app
COPY mvnw .
COPY .mvn .mvn
COPY pom.xml .
COPY src src
RUN ./mvnw install -DskipTests
RUN mkdir -p target/dependency && (cd target/dependency; jar -xf ../*.jar)
FROM eclipse-temurin:21-jre-alpine
VOLUME /tmp
ARG DEPENDENCY=/workspace/app/target/dependency
COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","com.example.demo.DemoApplication"]# ビルドと実行
docker build -t spring-boot-app .
docker run -p 8080:8080 spring-boot-appさらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。
まとめ:Spring Bootで実務レベルのアプリ開発を
Spring Bootは、Javaで効率的にWebアプリ開発できる最強フレームワークです。
✅ Spring Bootを学ぶべき理由
1. エンタープライズ標準
└─ 多くの企業で採用
2. 高い生産性
└─ ボイラープレート最小化
3. 豊富なエコシステム
└─ Spring Cloud等と連携
4. プロダクション対応
└─ 監視・運用機能標準搭載
5. 学習コスト低い
└─ 従来Springより簡単
6. 求人需要高い
└─ Java開発で必須スキル
7. コミュニティ活発
└─ 情報・サポート豊富今すぐSpring Bootを始めて、実務レベルのJava開発者に。
※本記事の情報は2025年11月時点のものです。最新情報はSpring Boot公式サイトでご確認ください。
さらに理解を深める参考書
関連記事と相性の良い実践ガイドです。手元に置いて反復しながら進めてみてください。







