Spring入門のコードには、これまでの章で自分では書かなかった書き方がいくつか出てきます。アノテーション、列挙型の定数、クラス名.class、static import などです。この章は、それらを 読めるようになる ための章です。自分で定義する練習はせず、Spring入門の実際のコードを読みながら意味を確かめます。手を動かす作業はありません。
この章で学ぶこと
- アノテーションの形と付ける場所
- 列挙型の定数(
GenerationType.SEQUENCE)の読み方 -
クラス名.classとstatic importの読み方 - メソッドを続けて呼ぶ書き方
- 型を2つ指定するジェネリクスと、インターフェースの継承
- Spring入門のクラスを実際に読む
13.1 アノテーション
第10章で使った @Override のように、@ で始まる印を アノテーション といいます。アノテーションは、クラス・フィールド・メソッド・引数などに付けて、「これはこういうものです」という 情報を添える ものです。アノテーション自体は処理を行いません。その情報を読み取った側(コンパイラー、Spring、JPA など)が、それに応じた動きをします。
| 形 | 例 | 意味(Spring入門での) |
|---|---|---|
@名前 |
@Entity、@Autowired、@Override
|
値を持たない。「このクラスはデータベースの表に対応する」「このフィールドには Spring が値を入れる」 |
@名前(値) |
@GetMapping("/employees") |
値を1つ持つ。「/employees という URL が開かれたら、このメソッドを呼ぶ」 |
@名前(属性 = 値, 属性 = 値) |
@Size(max = 20, message = "{name.size}") |
名前付きの値を持つ。「最大 20 文字。超えたらこのメッセージ」 |
アノテーションは、付けたい対象の 直前(ふつうは1つ上の行)に書きます。クラス・フィールド・メソッドに複数付けるときは行を分けて並べ、引数に付けるときは @Validated @ModelAttribute RegisterForm registerForm のように同じ行に並べます。
@NotBlank(message = "{name.notBlank}")
@Size(max = 20, message = "{name.size}")
private String name;
これは「name は空であってはならず、最大 20 文字」という入力チェックの情報を、フィールドに添えています。チェックを実際に行うのは Spring です。
アノテーションもクラスと同じく、パッケージに属しています。@Override は java.lang なので import が不要ですが、Spring や JPA のアノテーションは import が必要です。補完(@Ent と入力して Ctrl + Space)で入力すると、import も自動で入ります。
【知識メモ】アノテーションの意味は、使う仕組みごとに覚える
@Override の意味は Java が決めていますが、@Entity や @GetMapping の意味は、JPA や Spring が決めています。Java の文法として覚えるのは「@名前(…) の形で情報を添える」ことだけで、個々の意味は Spring入門でそのつど学びます。
13.2 列挙型の定数
Spring入門の Entity には、次の行があります。
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_EMP")
GenerationType.SEQUENCE は、列挙型(enum)の定数です。列挙型は、「決まったいくつかの値のどれか」を表す型で、GenerationType には SEQUENCE・IDENTITY などの値が用意されています。
-
型名.定数名の形で書く。第9章のstaticな定数Employee.MAX_AGEと同じ読み方 - 定数名は、すべて大文字(第4章の定数の決まりと同じ)
- 列挙型も
importが必要。補完で入れる
この教材と Spring入門では、列挙型を自分で定義することはありません。「型名.大文字の名前 は、用意された値の1つを選んでいる」と読めれば十分です。
13.3 クラス名.class
Spring入門のアプリを起動するクラスには、次の行があります。
SpringApplication.run(EmployeeAppApplication.class, args);
EmployeeAppApplication.class は、「EmployeeAppApplication というクラスそのもの」を表す値です。オブジェクト(new で作ったもの)ではなく、クラスの定義そのものを、メソッドの引数として渡しています。SpringApplication.run は、受け取ったクラスを起点にしてアプリを起動します。
SpringApplication.run(…) は、第7章で学んだ クラス名.メソッド名(…) の形の、static なメソッドの呼び出しです。args は main メソッドの引数(第6章)をそのまま渡しています。
13.4 static import
Spring入門のテストのコードには、次の import があります。
import static org.junit.jupiter.api.Assertions.assertEquals;
import static は、別のクラスの static なメソッドを、クラス名を付けずに呼べるようにする 書き方です。本来は Assertions.assertEquals(3, list.size()) と書くところを、assertEquals(3, list.size()) と短く書けます。
読むときは、「クラス名が付いていないのに、このクラスに無いメソッドを呼んでいる」行を見つけたら、ファイルの先頭の import static を確かめます。この教材と Spring入門の本編では使わず、Spring入門の第7章 7.3(任意の節。JUnit のテスト)に出てくるだけです。
13.5 メソッドを続けて呼ぶ
第11章で employees.get(0).getName() という書き方が出てきました。Spring入門には、次のような行があります。
updateForm.setDeptId(employee.getDepartment().getDeptId());
メソッドが返したオブジェクトに対して、続けて . でメソッドを呼べます。左から順に 読みます。
-
employee.getDepartment()… 社員の部署(Departmentのオブジェクト)を取り出す -
.getDeptId()… その部署の部署IDを取り出す -
updateForm.setDeptId(…)… 取り出した部署IDを、フォームに入れる
途中のメソッドが null を返すと、その次の . で NullPointerException が起きます(第8章)。スタックトレースの Cannot invoke "…" because the return value of "…" is null というメッセージは、この形で起きたときのものです。
13.6 型を2つ指定するジェネリクス
Spring入門の Repository は、次のように定義します。
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
List<Employee> findAllByOrderByEmpIdAsc();
Employee findByEmpId(Integer empId);
}
| 部分 | 読み方 | 学んだ章 |
|---|---|---|
@Repository |
インターフェースに付けたアノテーション。「このインターフェースはデータの読み書きを行う」と Spring に伝える | 13.1 |
public interface EmployeeRepository |
インターフェースの定義 | 第10章 |
extends JpaRepository<…> |
インターフェースが、別のインターフェース JpaRepository を継承する |
第10章 |
<Employee, Integer> |
型を2つ指定するジェネリクス。「扱う Entity は Employee、その主キー(社員ID)の型は Integer」。, で区切る |
第11章 |
List<Employee> findAllByOrderByEmpIdAsc(); |
中身の無いメソッドの宣言。Employee のリストを返す。中身は Spring が用意する |
第10章・第11章 |
Employee findByEmpId(Integer empId); |
社員IDを受け取り、Employee を1つ返す(見つからなければ null) |
第7章・第11章 |
ジェネリクスの型が2つになっても、読み方は同じです。< > の中の型が何を表すかは、そのクラスやインターフェースごとに決まっています。
13.7 Spring入門のクラスを読む
Spring入門の第4章で最初に書く HelloController を、この教材で学んだことだけで読んでみましょう。
package com.example.employeeapp.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import com.example.employeeapp.component.GreetingMessage;
@Controller
public class HelloController {
@Autowired
private GreetingMessage greetingMessage;
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("message", greetingMessage.getMessage());
return "hello";
}
}
| 行 | 読み方 | 学んだ章 |
|---|---|---|
package com.example.employeeapp.controller; |
現場で使われる形のパッケージ名 | 第9章 |
import org.springframework…; |
Spring のクラスとアノテーションを使うための import。補完で自動で入る |
第9章 |
@Controller |
クラスに付けたアノテーション。「このクラスは画面の要求を受け付ける」と Spring に伝える | 13.1 |
@Autowiredprivate GreetingMessage greetingMessage;
|
private なフィールド。値は new せず、@Autowired によって Spring が入れる。書き忘れると null のままで NullPointerException
|
第8章・第9章 |
@GetMapping("/hello") |
メソッドに付けた、値を1つ持つアノテーション | 13.1 |
public String hello(Model model) |
Model 型の引数を受け取り、String を返す public なメソッド。static は付いていないので、オブジェクト(Spring が作る)に対して呼ばれる |
第7章・第8章 |
model.addAttribute("message", greetingMessage.getMessage()); |
greetingMessage の getMessage() が返した文字列を、model の addAttribute に渡す。引数の中でメソッドを呼んでいる |
第7章 |
return "hello"; |
戻り値は画面の名前 | 第7章 |
Spring 特有の意味(@Controller が何をするか、Model とは何か)は Spring入門で学びますが、Java の文法としては、すべてこの教材で学んだ形 です。Spring入門のコードに出てくる Java の書き方で、この教材に無いものはありません。
13.8 まとめと確認問題
まとめ
-
アノテーション は
@名前・@名前(値)・@名前(属性 = 値, …)の形で、クラス・フィールド・メソッドの直前に付けて情報を添える。意味は、それを読む仕組み(Spring、JPA)が決める。importが必要 -
列挙型 の定数は
型名.定数名(GenerationType.SEQUENCE)。用意された値の1つを選ぶ -
クラス名.classは、クラスそのものを表す値 -
import staticは、staticなメソッドをクラス名なしで呼べるようにする -
a.getB().getC()は左から順に読む。途中がnullならNullPointerException -
JpaRepository<Employee, Integer>は型を2つ指定するジェネリクス。インターフェースはインターフェースをextendsできる
この章で使った書き方は、付録B「文法早見表」の「メソッドとクラス」 にまとめています。
確認問題
問1 @Size(max = 20, message = "{name.size}") は、アノテーションの3つの形のどれですか。
解答を見る
名前付きの値を持つ @名前(属性 = 値, 属性 = 値) の形です。max と message が属性です。
問2 GenerationType.SEQUENCE の SEQUENCE がすべて大文字なのはなぜですか。
解答を見る
列挙型の定数だからです。定数の名前はすべて大文字で書く決まりです。
問3 employee.getDepartment().getDeptName() は、どの順番で何をしていますか。
解答を見る
まず employee.getDepartment() で社員の部署のオブジェクトを取り出し、次にそのオブジェクトの getDeptName() で部署名を取り出します。
問4 JpaRepository<Department, Integer> の2つの型は、それぞれ何を表していますか。
解答を見る
1つ目の Department は扱う Entity のクラス、2つ目の Integer はその主キー(部署ID)の型です。
問5 @Autowired を書き忘れると、どんな例外が起きますか。理由も答えてください。
解答を見る
NullPointerException です。Spring がフィールドに値を入れてくれなくなるので、フィールドが null のままになり、そのメソッドを呼んだときに起きます。