Spring Bootについて学んだことのまとめ

# はじめに
去年の11月～12月にSpringBootを初めて触った時に学んだことを備忘録として残します。
※あくまで備忘録です

# 対象読者
Spring Bootをこれから触ろうって方向けです。使ったことのある方には既知の内容になるかと思います。

# 作ったもの
受け取った入力値をもとに情報を返すだけの簡単な外部公開API。

# 使用した諸々
* Java(1.8)
* Spring Boot(2.1.0)
* Spring Data JPA
* Maven(3.5.3)
* Lombok(1.18.2)


# 今回実装したアプリの構成
![クラス構成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588397/c8180151-6e49-425f-ffec-dac0616c7c7e.png)

# 各種クラスの概要

| クラス名 | 概要 |
| --- | --- |
| RestControllerクラス | APIのインターフェース部分を実装するクラス。 |
| Formクラス | クライアントからのリクエストの内容や返すレスポンスの情報を保持するクラス。 |
| RestControllerAdviceクラス | RestController共通の処理を実装するクラス。 |
| Serviceクラス | 業務ロジックを実装するクラス。 |
| Repositoryクラス | DBアクセスを行うクラス。 |
| Entityクラス| DBから取得したデータやDBに投入するデータを保持するクラス。 |

# 各種クラスの説明とサンプル
### Mainクラス<br>
`@SpringBootApplication`を付与する。
※以下の3つのアノテーションを付与した状態と等価。

|アノテーション|概要|
|---|---|
|`@EnableAutoConfiguration`|Spring Bootの自動設定がオンになり、定義した依存関係に従って自動で各種設定を行ってくれる。|
|`@ComponentScan`|コンポーネントスキャンが実行され、スキャン対象パッケージ内の`@Component`が付与されたクラスが自動で読み込まれてDIコンテナで管理される。コンポーネントスキャンしたクラス同士は`@Autowired`により変数にセットすることが出来る。※後述のサンプルコード参照|
|`@Configuration`|付与することで、個別にBeanを登録したり、設定クラスを読み込むことが出来る。|


`@ComponentScan`と`@EnableAutoConfiguration`により各種クラスの読み込み、設定が行われ、アプリケーションが実行される。
※デフォルトだと`@ComponentScan`が付与されたクラスと同階層のクラス、及びそれより下の階層のパッケージがスキャン対象になるため、`@ComponentScan`が付与されたクラスより上の階層のクラスは読み込まれない。

```Java:SampleApplication.java
@SpringBootApplication
public class SampleApplication {

    public static void main (final String[] args) {
        SpringApplication.run (SampleApplication .class, args);
    }

    @Override
    protected SpringApplicationBuilder configure (final SpringApplicationBuilder builder) {
        return builder.sources (SampleApplication .class);
    }
}
```


### RestControllerクラス<br>
* `@RestController`アノテーションを付与する。`@ComponentScan`の対象。
* APIのインターフェース部分を実装するクラス。入力値の精査や各種コンポーネントの呼び出しを行い、クライアントにレスポンスを返す。
* 各メソッドにHTTPメソッドやURLのマッピングを定義し、リクエストと呼び出されるメソッドのマッピングを行う。
* 受け取ったリクエストや返すレスポンスを扱うためにどのFormクラスを使うかは各メソッドごとに定義する。

```Java:SampleController.java
/**
 * サンプルコントローラ。
 */
@RestController
@Validated
public class SampleController {

    @Autowired
    private SampleService service; //使用するServiceクラスを紐づける。

    @PostMapping (path = "/sample", //メソッドにマッピングするパスを指定する。
        consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, //受け取るリクエストのMediaTypeを指定する。
        produces = MediaType.APPLICATION_JSON_UTF8_VALUE) //返すレスポンスのMediaTypeを指定する。
    @CrossOrigin
    public ResponseEntity<SampleForm> sampleMethod (
        @RequestHeader (value = "sample_key") //ヘッダーから受け取る値のキー値を定義する。
        @NotEmpty (message = "sample_key is empty.") //Formを使用せずに精査する場合はここに精査内容を定義する。
        @Valid String sampleKey, //ヘッダーから受け取った値を格納する変数を定義する。
        @RequestBody
        @Valid SampleForm form //ボディから受け取った値を格納するFormクラスと格納先の変数を定義する。
    ) {
        return new ResponseEntity<> (service.sampleMethod (form.getSampleUserId), HttpStatus.OK);
    }
}
```

### Formクラス
* クライアントからのリクエストの内容や返すレスポンスの情報を保持するクラス。
* 精査内容等はここに記述する。
* アノテーションによる精査定義のほか、独自の精査用メソッドを定義することも出来る。

```Java:SampleForm.java
@Data
@ToString
public class SampleForm implements Serializable {

    private static final long serialVersionUID = 1L;

    @JsonProperty ("sample_user_id") //JSON上でのキー値を定義する。
    @NotEmpty (message = "sampleUserId is empty") //精査内容を定義する。
    private String sampleUserId;
    
}
```

### RestControllerAdviceクラス
* `@RestControllerAdvice`を付与する。`@ComponentScan`の対象。
* RestControllerクラスに共通の処理を実装出来る。（事前処理やエラーハンドリング等）
* `@ExceptionHandler`を付与したメソッドを用意することで、指定した例外が送出された際に自動でエラーハンドリングしてくれる。

```Java:SampleExceptionHandler.java
@RestControllerAdvice
public class SampleExceptionHandler {

    @ExceptionHandler (Exception.class) //キャッチする例外を定義する。
    @ResponseStatus (HttpStatus.INTERNAL_SERVER_ERROR) //上記例外発生時に返すレスポンスのHTTPステータスコードを定義する。
    protected ErrorForm handleInternalServerError (Exception e) {
        logger.error (e);
        return new ErrorForm ("Internal Server Error");
    }

    @RequiredArgsConstructor
    private class ErrorForm implements Serializable {
        private static final long serialVersionUID = 1L;
        private final String message;
    }
}
```

### Serviceクラス<br>
* `@Service`を付与する。`@ComponentScan`の対象。
* 業務ロジックを実装するクラス。RestControllerクラスから受けとった入力値をもとに、各種計算やDBアクセスを行う。
* 今回はSpring Data JPAを使ったのでリポジトリクラスを呼び出すことでDBアクセスを行う。

```Java:SampleService.java
@Service
@Transactional //例外発生時に自動ロールバックする。
public class SampleService {

    @Autowired
    private SampleUserRepository sampleUserRepository; //使用するRepositoryクラスを紐づける。

    public SampleForm returnSampleForm (String sampleUserId) {
        return convertToSampleForm(sampleUserRepository.findBySampleUserId(sampleUserId));
    }
}
```

### Repositoryクラス<br>
* `@Repository`を付与する。`@ComponentScan`の対象。
* DBアクセスを行うクラスで、Spring Data JPAを使うとinterfaceだけ実装すれば簡単なCRUDなら実行することが出来る。
* JpaRepositoryを継承させ、アクセスするテーブルを定義する。
* 命名規約に従ってメソッド名を付けることで、自動的にCRUDのどれなのか、どんな検索条件かを読み取ってくれる。<br>
例）findByUserId(String userId) とすると、userIdで検索を行うメソッドとなる。
* クエリを実装したりテーブル結合を伴う検索をしようとするとここまで単純にはいかないが、今回は使用しなかったため割愛。

```Java:SampleUserRepository.java
@Repository
public interface SampleUserRepository extends JpaRepository<SampleUser, String> { //アクセス先のテーブルと主キーの型を定義する。
    SampleUser findBySampleUserId (String sampleUserId); //命名規約に従ってメソッド名と引数を定義する。
}
```

### Entityクラス<br>
* `@Entity`を付与する。
* DBから取得したデータやDBに投入するデータを保持する。
* 今回はテーブルと1対1に紐づかせた。

```Java:SampleUser.java
@Entity
@Table (name = "sample_user") //紐づけるテーブル名を定義する。
@Data
public class SampleUser implements Serializable {

    private static final long serialVersionUID = 1L;

    /** サンプルユーザID */
    @Id //主キーの項目に付与する。
    @GeneratedValue (strategy = GenerationType.IDENTITY) //自動採番させる場合は採番方法を定義する。
    private String sampleUserId;

    /** サンプルユーザ名*/
    private String sampleUserName;
}
```

# 参考サイト
* https://docs.spring.io/spring-boot/docs/current/reference/html/index.html