🎉

【超入門】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」の基本的な構成を、各コンポーネント別にミニサンプルコードをのもとに見ていきます。

  1. InputResource / OutputResource
    InputResource: APIに渡されるリクエストのデータ
    OutputResource: APIのレスポンスとして返すデータ
InputResource
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; }
}
OutputResource
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; }
}
  1. InputDto / OutputDto
    DTO(Data Transfer Object)は内部処理用のデータクラス
InputDto
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; }
}

OutputDto
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; }
}
  1. Repository.java
    データアクセスのインターフェース。
    シンプルな例では、Spring Data JPAのリポジトリを想定。
Repository
import org.springframework.data.jpa.repository.JpaRepository;

public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
    // 追加のクエリは必要に応じて定義
}

  1. Service.java (インターフェース)
    ビジネスロジックの抽象化。
Service
public interface MyService {
    OutputDto updateEntityName(Long id, String newName);
}
  1. ServiceImpl.java
    ビジネスロジックの実装。
ServiceImpl
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());
    }
}
  1. Controller
    REST APIのエンドポイント。
Controller
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");
    }
}
  1. Entity(JPAエンティティ)例
MyEntity
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