【超入門】Spring Boot WEB AP開発🌱
はじめに
Springとは?
Java言語でWebアプリケーションを開発する際、最もよく使われるフレームワークの一つが「Spring(スプリング)」です。
Spring Frameworkは、複雑になりがちなJavaアプリの設計や開発を効率的・安全に進めるための、さまざまな便利機能を備えたライブラリ群です。
Spring Boot とは?
「Spring Boot(スプリング・ブート)」とは、Spring Frameworkをさらに簡単・手軽に扱えるよう拡張したもので、近年WEB開発で主流になっているものです。
Spring Bootなら、面倒な初期設定を省き、とても簡単にWebアプリやREST APIの開発を始めることができます。
Webアプリケーション開発の基本構成
Controller/Service/Repository 各層の役割・関係性
①Controller(コントローラー)層:【受け口・振り分け】
役割
ユーザーや外部システムからのリクエストを受け取る窓口です。
入力値のチェックやリクエスト内容の整理を行い、サービス層(Service)に処理を受け渡します。
サービス層から処理結果やデータを受け取り、レスポンスとして返す(HTMLやJSONなど)。
②Service(サービス)層 :【業務処理の本体】
役割
アプリケーションのビジネスロジック(業務処理)を担う中心的な層です。
コントローラー層からデータを受け取り、必要ならバリデーションや計算等の本質的な処理を行う。
データの永続化や取得が必要な場合にはリポジトリ層(Repository)に処理を依頼する。
複数のリポジトリやサービスを統合した処理も担当することが多い。
③Repository(リポジトリ)層 : 【データの出入り口】
役割
主にデータベースなど永続化層へのアクセスや管理を行う層です。
サービス層からの依頼で、データのCRUD処理(作成・読み取り・更新・削除)を担当します。
実際のSQL実行やORマッピングもここで行われます。
この分離により、責任範囲が明確になり、保守性・テスト性の向上や役割ごとの再利用がしやすくなります。
Dto(データ転送オブジェクト)とは?
Dto(Data Transfer Object:データ転送オブジェクト)は、システム内部の層間でデータをやり取りするためのシンプルなデータ構造です。
●データをまとめて運ぶための「入れ物」
●ビジネスロジックや振る舞い(メソッド)はほとんど持たない
●複数の値(プロパティ)を一度に渡すのに便利
(イメージ)
コントローラーは主にリクエストデータ(例:POSTのボディ)をサービス層に渡します。
この時、リクエスト内容を「Dto」として受け取り、そのDtoをサービス層の処理に渡します。
主要なアノテーションと基本用語
●アノテーション(Annotation)とは
アノテーションは、Javaのソースコードに特別な意味やメタデータを追加する仕組みです。@マークで宣言し、クラスやメソッド、フィールドなどにつけます。
@Override
public void foo() {}
Springでは独自のアノテーションがたくさんあり、プログラムに「これはこういう役割ですよ」と知らせたり、動的な設定をします!
よく使うアノテーションを表にまとめてみました!
1.アプリケーション基盤
アノテーション | 説明 |
---|---|
@SpringBootApplication | Spring Bootのアプリ起動クラスに付ける。構成を自動設定し、Component Scan、AutoConfigurationなどを有効化。 |
2.DI/Bean定義
アノテーション | 説明 |
---|---|
@Component | Beanとして登録。DI対象のクラスやコンポーネントに付ける。 |
@Service | サービス層のBean登録。実態は@Componentと同じだが、意味合いを明確に。 |
@Repository | データアクセス層のBean登録(例外変換もあり) |
@Controller | コントローラー層のBean登録 |
@RestController | RESTコントローラ(@Controller + @ResponseBodyの短縮) |
@Configuration | 設定クラスであることを明示。Java ConfigでBean登録する場合に使用。 |
@Bean | 設定クラス(@Configuration)のメソッドで使います。戻り値をSpring Beanとして定義。 |
※どれも最終的に「Springが自動でBean化する」効果を持っています。「役割によって使い分けてね!」というイメージ
3.DI 補助
アノテーション | 説明 |
---|---|
@Autowired | フィールドやコンストラクタ、SetterのDI自動注入を行う。 |
@Qualifier | DI時、同タイプ複数Beanが存在する際に注入対象を特定。 |
@Value | プロパティの値を注入する。@Value("${app.name}") など。 |
4.Web/REST
アノテーション | 説明 |
---|---|
@RequestMapping | HTTPリクエストのエンドポイントや方法(GET/POST等)をマッピング(AとBを対応付けること) |
@GetMapping | GETリクエスト専用のショートカット。 |
@PostMapping | POSTリクエスト専用。 |
@PutMapping | PUTリクエスト専用。 |
@DeleteMapping | DELETEリクエスト専用。 |
@PathVariable | URLパスの変数部分を引数にバインド(何かと何かを「ひも付ける」「自動的にセットする」こと)。 |
@RequestParam | リクエストパラメータを引数にバインド。 |
@RequestBody | リクエストボディ(JSON等)をJavaオブジェクトにバインド。 |
@ResponseBody | 戻り値をHTTPレスポンスボディとして返す。 |
Bean/Bean化ってなに?
Bean(ビーン)とは?
Beanは、
「Springが自動で管理してくれるインスタンス」
のことです。
通常、DI(Dependency Injection)の対象となる部品です。
→Bean化されることで、DIできるようになります!!
普段、Javaではインスタンスを手動で作ります・・・!
MyService service = new MyService();
でも、Springではこの作業をSpring自身に任せることができます!
Bean化とは?
「Bean化する」というのは、
「Springがインスタンスを自動作成し管理できるように登録する」
という意味です。
たとえば、
@Service
public class MyService {
// ...
}
と書くと、MyServiceがBean化され、
Springが必要なタイミングでこのMyServiceを用意してくれます!
Spring Bootでは、主に以下のようなアノテーションをつけることでBean化されます。
@Component
@Service
@Repository
@Controller
@Configuration
クラス内 @Bean
アノテーション
DI(依存性注入)ってなに?
プログラミングにおいては、あるクラス(たとえばA)が別のクラス(たとえばB)を使いたいとき、普通はこう書きます。
public class A {
private B b = new B(); // 自分でBのインスタンスを作っている
}
このやり方だとAとBが密結合になります。後でBを別のクラスに差し替えるのが難しいです。
DI (依存性注入)では、「AはBが必要ですよ」とSpringに伝えます。するとSpringがBの用意と注入(インジェクション)を"自動で"やってくれます。AがBに直接依存しません。
DIとは、他のBean(部品)を自動で渡してもらう仕組みです。
例)UserServiceがUserRepository(別のBean)に依存する場合
@Component
public class UserService {
private final UserRepository repository;
@Autowired
public UserService(UserRepository repository) {
this.repository = repository;
}
}
ここで、SpringがUserRepositoryのBeanを作って、自動的にUserServiceのコンストラクタへ渡してくれます。
Spring Boot でのDI活用の流れ
依存先クラス(サービスなど)を作る
↓
DIして使いたい側のクラスで@Autowired
やコンストラクタインジェクションを書く
↓
Spring Bootが自動でインジェクトしてくれる
ミニサンプルコード
たとえ話として、コーヒーメーカーを例にしてみます!
DripCoffeeMaker クラスは、CoffeeBean クラスに依存しています。
自分で「コーヒー豆」を作るのではなく、外から豆を「渡して」もらいます。
もしDripCoffeeMakerが自分で「コーヒー豆の銘柄」や「原産地」まで決めていたら、変更したいときにコードを書き換える必要があります
でも、豆を「誰か外部が用意して入れてくれる」仕組みがあれば、あとからいろんな豆に変えられます。
これが『DI』の発想です。
@Component
public class CoffeeBean {
private String name = "Ethiopia";
public String getName() { return name; }
}
// コンストラクタインジェクション
@Component
public class DripCoffeeMaker {
private final CoffeeBean bean;
@Autowired
public DripCoffeeMaker(CoffeeBean bean) {
this.bean = bean;
}
public void brew() {
System.out.println("Brewing " + bean.getName() + " Coffee");
}
}
★ポイント
CoffeeBean、DripCoffeeMakerどちらも @Component
でBean化。
DripCoffeeMakerがCoffeeBeanに依存していることを宣言し、@Autowired
で注入。
↓Spring Boot だとこうなる
@SpringBootApplication
public class CoffeeApp {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(CoffeeApp.class, args);
DripCoffeeMaker maker = context.getBean(DripCoffeeMaker.class);
maker.brew(); // => Brewing Ethiopia Coffee
}
}
実際に書くのは「どんな豆を注入するか」だけでOK!
受け取る側(DripCoffeeMaker)は「もらえます!」と宣言するだけ。
Springがインスタンス同士を自動でつなげてくれます。
サンプルで学ぶ 最小Web APIの実装例
各構成要素の概要
要素 | 説明 |
---|---|
Controller | 外部リクエスト(API等)受付・応答 |
Service | ビジネスロジック定義(インターフェース)処理はしない |
ServiceImpl | Serviceの具体的な実装クラス |
Repository.xml | SQLやマッピング定義ファイル(MyBatis等を使う場合) |
Repository.java | 永続化レイヤ(DBとのやり取り)の定義(インターフェース) |
InputResource | 入力値(リクエスト)を受け取るリソースクラス |
OutputResource | 出力値(レスポンス)に変換して返すリソースクラス |
InputDto | サービスやリポジトリに受け渡す入力データ用DTO |
OutputDto | サービスやリポジトリから受け取る出力データ用DTO |
Entity | DBにマッピングされるJPAエンティティ。 |
オブジェクト間の全体のイメージ
クライアント(JSON)
↓
【Controller】
InputResourceに自動変換
↓(manual変換例:InputDtoに変換)
【Service】(ビジネスロジック)
↓(リポジトリ呼び出し)
【Repository】(DBアクセス時)
↓
【MyEntity】(DBエンティティ)
↓(エンティティからOutputDtoに変換)
【ServiceImpl】(結果のOutputDtoを受け取り)
↓(変換)
【OutputResource】(APIレスポンス用)
↓
クライアント(JSONで返す)
サンプルコード
「DBから情報を取得して変更するAPI」の基本的な構成を、各コンポーネント別にミニサンプルコードをのもとに見ていきます。
- InputResource / OutputResource
InputResource: APIに渡されるリクエストのデータ
OutputResource: APIのレスポンスとして返すデータ
public class InputResource {
private Long id; // 更新対象のID
private String newName; // 新しい名前
// getter/setter
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getNewName() { return newName; }
public void setNewName(String newName) { this.newName = newName; }
}
public class OutputResource {
private Long id;
private String name;
private String message;
public OutputResource(Long id, String name, String message) {
this.id = id;
this.name = name;
this.message = message;
}
// getter
public Long getId() { return id; }
public String getName() { return name; }
public String getMessage() { return message; }
}
- InputDto / OutputDto
DTO(Data Transfer Object)は内部処理用のデータクラス
public class InputDto {
private Long id;
private String newName;
public InputDto(Long id, String newName) {
this.id = id;
this.newName = newName;
}
// getter
public Long getId() { return id; }
public String getNewName() { return newName; }
}
public class OutputDto {
private Long id;
private String name;
public OutputDto(Long id, String name) {
this.id = id;
this.name = name;
}
// getter
public Long getId() { return id; }
public String getName() { return name; }
}
- Repository.java
データアクセスのインターフェース。
シンプルな例では、Spring Data JPAのリポジトリを想定。
import org.springframework.data.jpa.repository.JpaRepository;
public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
// 追加のクエリは必要に応じて定義
}
- Service.java (インターフェース)
ビジネスロジックの抽象化。
public interface MyService {
OutputDto updateEntityName(Long id, String newName);
}
- ServiceImpl.java
ビジネスロジックの実装。
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class MyServiceImpl implements MyService {
private final MyEntityRepository repository;
public MyServiceImpl(MyEntityRepository repository) {
this.repository = repository;
}
@Override
@Transactional
public OutputDto updateEntityName(Long id, String newName) {
// DBからエンティティ取得
MyEntity entity = repository.findById(id)
.orElseThrow(() -> new RuntimeException("Entity not found"));
// 名前を更新
entity.setName(newName);
// 更新を保存
repository.save(entity);
// DTOに変換して返却
return new OutputDto(entity.getId(), entity.getName());
}
}
- Controller
REST APIのエンドポイント。
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/entities")
public class MyController {
private final MyService myService;
public MyController(MyService myService) {
this.myService = myService;
}
@PostMapping("/update")
public OutputResource updateEntity(@RequestBody InputResource input) {
// InputResourceを元にInputDtoを作成
InputDto inputDto = new InputDto(input.getId(), input.getNewName());
// ビジネスサービス呼び出し
OutputDto outputDto = myService.updateEntityName(inputDto.getId(), inputDto.getNewName());
// OutputResourceに変換して返却
return new OutputResource(outputDto.getId(), outputDto.getName(), "Update successful");
}
}
- Entity(JPAエンティティ)例
import javax.persistence.*;
@Entity
@Table(name = "my_entity")
public class MyEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// getter/setter
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
【重要ポイント】
層の分離によって、外部仕様と内部実装の変更を影響範囲に限定できます!
変換(マッピング)は、層の境界で適宜行う。
明確な役割分担により、保守性と拡張性が高まる。
さいごに
この記事では、Spring FrameworkやSpring Bootの基本から、Webアプリケーション開発における層構造(Controller/Service/Repository)や各層の役割、またDTOやエンティティを用いたデータの流れについて解説しました。そして、実際のコード例を通じて、最小構成のWeb APIの作成方法を学びました。
▶層構造の意義
Controller:外部リクエストの受け口。リクエスト内容を整理し、適切に中間層(Service)に受けわたす。
Service:ビジネスロジックの本体。複雑な処理や複数リポジトリの調整を担う。
Repository:DB層へのアクセスを専門的に担当。
→各層を分離することで役割が明確になり、保守性や拡張性が格段に向上します。
▶DI(依存性注入)とBean化の重要性
Springの「依存性注入(DI)」により、クラス間を緩やかに結合し、柔軟かつ再利用可能な設計を実現。
Bean化によって、Springがインスタンスを作成・管理してくれるおかげで、コードが簡潔化し、開発効率が大幅に向上します。
▶DTOによる層間のデータ転送
層間で役割を分離するだけでなく、それぞれ専用のデータ構造(DTO/Resource)を利用することで、内部実装と外部仕様の切り離しが可能になります。
▶コード例から学んだこと
必要最小限のスケルトンを用いてAPIを構築する流れを学びました。
Spring Bootでは、リクエストからレスポンスまでの全体のデータの流れを明確に記述することで、疎結合で保守性の高いコードを書くことができます。
◆まとめ◆
Spring Bootは、Webアプリケーションを効率よく、安全に柔軟な設計で開発できる強力なツールです。
最初は複雑に感じますが、Springのフレームワークは層ごとに明確な責任を持たせ、シンプルなコードを書くことができます。
実際にプロジェクトで触れて、動かしながら感覚を身に着けていきたいです!👍
Discussion