Spring Bootで簡単なWeb APIを作成する
続いて、先ほど作成したプロジェクトを使い、図6に示す簡単なWeb APIのエンドポイントを追加しつつSpring Bootでの開発の流れを紹介します。
サンプルの仕様は、(1)のように設定ファイルがあり、その設定ファイルで変数の初期値を指定します。次に、(2)のようにその変数の値を保持するアプリケーションで1つの共有インスタンス(シングルトンインスタンス)を保持します。
そして、(3)のようにそのインスタンスをJSON形式でGETできるAPIエンドポイントと、POSTで変更できるAPIエンドポイントがあります。リスト3はこれらのAPIを利用したときの実行例です。
$curl -s http://127.0.0.1:8080/hello/world
{
"value": "Hello sample"
}
$curl -s -H 'Content-Type:application/json' -d '{ "value" : "こんにちは" }' http://127.0.0.1:8080/hello/world
{
"value": "こんにちは"
}
$curl -s http://127.0.0.1:8080/hello/world
{
"value": "こんにちは"
}
また、これらのソースが図7のようなパッケージ、もしくはフォルダ構造になっています。
ここでは、必ずメインプログラムであるSampleApplicationクラスのパッケージ以下に関連クラスを作成するように気を付けてください。
SpringBootではそれぞれのクラスに付与されたアノテーションから自動的に管理できるようにしていますが、そのためにはプロジェクトのメインパッケージ配下に置く必要があります。
ただし、これらの制限はあくまでデフォルトの設定ではあり、これらの変更方法は次回以降説明します。現時点ではSpring Bootで特別な設定が必要ないように、こういったルールでコードを記述するようにしてください。
アプリケーションでの設定(DI)を制御する
Springでの設定では、設定ファイルから値を取得するといった単純なものだけではなく、DIを実現するためのコードの記述もすることになります。
前回、DIを実現する方法として、XMLを使う以外にJavaのコードでDIの設定ができると紹介しました。リスト4がSpring FrameworkのDI設定例です。
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
// (1) Javaベースの設定を示すアノテーション
@Configuration
public class SampleConfiguration {
// (2) 設定ファイルから"sample.world"の値で設定する
@Value("${sample.world:World}")
String world;
// (3) オブジェクトの作成方法を指定する
@Bean
public WorldModel worldModel(){
WorldModel model = new WorldModel();
model.setValue(world);
return model;
}
}
クラス宣言の前に(1)のように@Cofigurationアノテーションを指定します。この指定がDIのためのコードであることを指定します。続いて、(2)では設定ファイルの"sample.world"という項目の値を取得し、変数に設定しています。
また、設定がない場合のデフォルト値の指定は、:(コロン)以降に記述します。(3)ではこのアプリケーション内でのWorldModelの共有インスタンスを示す@Beanアノテーションを指定します。
このような流れでDIの定義をしていきます。DIの指定方法がJavaのコードになってしまったことで後から柔軟に実装を入れ替えることができなくなり、DIを使わない頃のJavaに先祖返りしてしまった印象を受けるかもしれません。
しかし、実際にはXMLでの柔軟すぎる変更の可能性やそれに伴う複雑なXML記述というのはデメリットも多々ありました。
そのため、Javaにすることでもう一度、開発者にコントロールを戻しています。ただし、開発者はあくまで設定という視点でわかりやすいコードを記述するように心がける必要があります。
Web APIのエンドポイントを定義する
Web APIのサンプルコードがリスト5です。同様のことを実現する際には、一般的にはServlet APIを使いますが、Springでは特定の親クラスを継承する必要がありません。
そのため、他のコードからの依存性を低く保ちつつ、また、テストが行いやすいコードとして記述しやすくなっています。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
// (1) Rest用のコントローラクラスであることを宣言する
@RestController
// (2) /hello 以下のリクエストに対する処理
@RequestMapping(value = "/hello")
public class HelloController {
private static final Logger log = LoggerFactory.getLogger(HelloController.class);
// (3) 自動的にWorldModelオブジェクトを設定する
@Autowired
WorldModel model;
// (4) GET /hello/world に対する処理
@GetMapping(value = "/world")
public WorldModel world(){
return model;
}
// (5) POST /hello/world に対する処理
@PostMapping(value = "/world")
public WorldModel setWorld(@RequestBody WorldModel world){
model.setValue(world.getValue());
log.info("set value : {}",model.getValue());
return world;
}
}
(1)ではRest用のコントローラであることを宣言しています。ここでのRestとはRESTFulという意味ではなく、単純にWebアプリケーションのコントローラである、といった程度の指定です。この指定によりWebコンテナが指定のURLで、どのコントローラを起動すればよいかを管理しています。
Springのプロジェクトの経験がある方であれば、Web用のコントローラクラスに@Controllerというアノテーションを使っていたと思いますが、Web APIのようにJSON形式のコントローラの場合には@RestControllerというアノテーションをつけることでリターン値をJSON形式のレスポンスデータとして扱うことができます。
また、(2)ではこのコントローラが/helloというエンドポイント配下に対する処理であることを定義しています。
続いて、(3)の@AutowiredというアノテーションはDIを実現するための指定でWorldModelのインスタンスが自動的に設定されます。
(4)では、/hello/worldに対してGETリクエストがあったときの処理を定義し、(5)ではPOSTリクエストがあったときの処理を定義しています。@RequestBodyというアノテーションは、リクエストデータとしてWorldModelのJSONフォーマットを利用することを宣言しています。
また、リクエストやレスポンスがJSONからJavaのオブジェクトに自動的に変換されていますが、JSONフォーマットへの変換方法にはデフォルトではJacksonを使っています。
そのため、必ずしも自分で指定したオブジェクトが正しくJSONにならなかった場合や、希望するフォーマットにカスタマイズする際には、Jacksonの仕様について知る必要があります。
最後に
Spring Bootを使えば、TomcatなどのWebコンテナの設定、起動、停止の方法、そして、開発したコードの設定方法などにわずらわされることなく、すぐにコードの記述、実行まで確認できます。
これらの簡略化は、Springが提供しているさまざまなアノテーションと、それらの設定やルールをまとめたSpring BootのStarterによって実現しています。
アノテーションの使い方やSpring特有の注意点などを覚える必要はありますが、Spring Bootはそれらを考慮しても十分使いやすくなっています。
また、利用する開発者が非常に多いために、ドキュメントやノウハウなども十分に蓄積されていることも安心できる大きな要因です。次回からは、もう少し本格的なサンプルを作成しながら各機能の紹介をしていきます。
参考資料

