最終更新日:2020/03/23 原本2019-05-10

Spring Bootで使われる基本的なアノーテーションと設定の仕組み

Spring Bootで作るマイクロサービス 第3回

 前回、Spring Bootを使って簡単なサンプルを紹介しましたが、今までのJavaプログラムを知っている方にとっては非常に少ないコードで実行できることに驚いた方もいるかもしれません。また、実装部分のコードがPOJO(Plain Old Java Object)クラスになっていることに気がつく方も多いことでしょう。一方で実装しているコードにはアノテーションが記述されています。Spring Bootではこれらのアノテーションの意味は非常に大切になります。今回はSpring Bootを利用する上で知っておくべき、基本となる部分の仕組みを簡単なサンプルアプリケーションを通じて紹介します。

サンプルアプリケーションの概要

 サンプルアプリケーションは図1に示すような簡単な住所録管理アプリケーションです。

図1:サンプルアプリケーションのイメージ
図1:サンプルアプリケーションのイメージ

 このアプリケーションでは、表1のAPIを提供します。

表1:提供するAPI
エンドポイント HTTPメソッド  概要   
/contact/add  POST  新規に連絡先データを登録する
/contact/list GET  登録してあるデータ一覧を取得する
/contact/item/{id} GET 指定したidの連絡先データ情報を取得する
/contact/item/{id}  POST  指定したidの連絡先データ情報を更新する
/contact/item/{id}  DELETE  指定したidの連絡先データを削除する

サンプルアプリケーションの実行方法

 サンプルコードはSpring Bootのアプリケーション(sample/java以下)とHTMLやJavaScriptなどのWebリソース(sample/web以下)の2つに分かれています。

 Webリソースは既にサーバ内にApacheなどの他のWebサーバが利用できるのであればそちらを利用するとよいでしょう。

 HTML側はサンプルアプリケーションがあるサーバと同じ場所でなくても動作するようになっています。その場合には、アクセスできる場所に配置できれば、どのようなパスであっても問題ありません。

 また、リスト1に記載されているAPIサーバへのアクセス先URLを変更してください。

[リスト1]APIサーバへのアクセスURL(web/js/contact.jsの抜粋)?? web/js/main.js
window.api  = "http://localhost:8081/contact";

 利用できるWebサーバがない場合には、設定ファイルをリスト2のように変更することで、アプリケーション「Tomcat」を起動し、これを通じてアクセス可能です。

 ただし、起動するポート番号を変更した場合には、前述したAPIサーバへのアクセス先URLも変更する必要があります。

[リスト2]サンプルアプリケーションの設定内容(java/src/main/resources/application.propertiesの抜粋)
app.web.prefix = /web
app.web.resource = <sample/webのあるフォルダ>
# 設定例 ( C:¥samples/web/ 以下にsample/web以下のHTMLソースを保存した時 )
# app.web.resource = file://C:/sample/web/

# tomcatを起動するポート
server.port = 8081

# (以下、省略)

 起動はリスト3のように行います。起動に成功したら、http://localhost:8081/web/index.htmlにアクセスしてください。

 ただし、WebリソースはES6のコードを含んでいますので、最新のES6対応のブラウザ(ChromeやEdgeなど)を利用してください。

[リスト3]サンプルアプリケーションの起動方法
./gradlew bootRun   ( MacLinuxの場合 )
gradlew.bat bootRun ( Windowsの場合 )

[Note]Spring Bootを使ったサーバサイドHTMLアプリケーションについて

 Spring BootでのJSPを使ったアプリケーションやHTMLテンプレートエンジンを使ったHTMLアプリケーションの開発も問題なく行えます。しかし、TomcatなどのWeb Application Server上で開発する方法に比べて実績やノウハウなどの情報は多くはありません。

 また、Webサイトのように日々の編集運用が必要になるものをSpring Bootを用いて開発するメリットは筆者はあまり感じませんでした。

 一方、Spring Bootが持つ他のサービスなどの自動設定機能を生かしたものとしては、筆者はマイクロサービスとして利用するAPIサーバなどが利用用途としてより適していると考えています。

 そのため、サンプルアプリケーションもHTML側の実装について意図的に分離してあります。また、今回はHTML側の実装については趣旨からはずれるため説明は行いません。

サンプルコードのソースコード構造

 サンプルアプリケーションがどのような動作をするのかがわかったところで、実際のソースコードの説明をしていきます。

 ソースコードは図2に示すように大きくわけて4つのグループで構成されていますが、処理のメインなるAPI部分の実装は、ContactControllerのみとなっています。

 しかしながら、処理のイメージがつかめないソースもいくつか存在しています。今回はソースコード全体にかかわる部分について紹介し、次回、個別の内容について紹介していきます。

図2:サンプルアプリケーションのソースコード一覧
図2:サンプルアプリケーションのソースコード一覧

Beanを登録するためのアノテーション

 Spring BootにおいてBean定義は、既存のサービスなどの設定でもあり、今回のAPI部分の実装などのロジックの実装でもあります。そのため、どのようなBean定義をしているのかを理解することは大変重要になります。

 また、Bean定義をする際にはアノテーションを利用するので、どのようなアノテーションを使えばよいのか知る必要があります。

 アノテーションは用途別に大きく分類され、また、各用途に応じて上書きするアノーテションが作られています。

 例えば、サンプルアプリケーションで利用しているBean定義のためのアノテーションの構造は3のようになっています。

図3:サンプルアプリケーションで利用しているアノテーションの構造
図3:サンプルアプリケーションで利用しているアノテーションの構造

 Bean定義に利用されるアノテーションは、表2に示すorg.springframework.stereotypeに定義してあるものが最も基本です。基本的にはこれらのアノテーションに機能的な違いはなく、管理上の慣習として使い分けが行われています。

 ただし、例外的にSpring MVCやSpring Dataなどのサブプロジェクトによっては特別な機能を持つクラスとして扱われる場合もあります。

表2:SpringフレームワークでBean定義のために使用する基本アノテーション
アノーテーション 概要
@Component DIコンテナにBeanとして登録するための最も基本的なアノテーションです。
@Configuration Bean定義をJavaベースで行うためのアノテーションです。
@Repository  データの保存や読み込みなどを扱うためのクラスを示すアノテーションです。Spring Dataなど使いデータベースを利用する場合には、このアノテーションを指定することで自動的にテーブルにアクセスできるようにするなど、特別な意味を持つケースがあります。
@Controller  MVCのコントローラクラスであることを示すアノテーションです。Spring MVCを使うWEBアプリケーションでは、このアノテーションを指定することでWebからのリクエストを処理するためのクラスとして利用されます。
@Service  ビジネスロジックなどを扱うためのクラスを示すアノテーションです。

 そして、各サブプロジェクトで特別なものとして、これらのアノーテーションを上書きして別のアノテーションが定義されます。例えば、サンプルアプリケーションでは表3のアノテーションを使っています。

表3:サンプルアプリケーションで利用している個別のアノテーション
アノーテーション  概要 
@SpringBootApplication  Spring Bootのアプリケーションのmainプログラムを指定するための慣習として利用されるアノテーションです。
@RestController  REST用のコントローラとして登録するためのアノテーション。
@RestControllerAdvice  各コントローラで共通の入力データ形式の変換やエラー処理などの共通処理を定義するためのアノテーションです。

 どんなアノテーションを利用すればよいのかといった情報は、公式ドキュメントなどを丁寧に読んでいけばわかりますが、実際には非常に大変です。

 一方で、それぞれのSpringのサブプロジェクトにはどのようなアノテーションがあり、また、どのような意味を持つのか一覧として参照できる情報もありません。

 そのため、網羅的にアノテーションの内容について理解することはなかなか大変な作業です。ただし、慣れてくれば利用するアノテーションはおおよそ同じものを使うことになるでしょう。

 その際に、多少わからないアノテーションなどに遭遇した場合でも、よく利用されるアノテーションのセットであることが多いのでアノテーション内のコードを参照する方が早いでしょう。

 例えば、@RestControllerのコードを参照すると、リスト4のようになっています。これらのコードより、@ControllerとしてのBean定義であり、@ResponseBodyも同時に定義したアノテーションであることがわかります。

[リスト4]@RestControllerのコード
package org.springframework.web.bind.annotation;
// (省略)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
  // (省略)
}

 また、同一パッケージ内に同じ利用シーンで使われる別のアノテーションなども定義してあるので、これらのJavaDoc情報などを参照にすれば、どのようなアノテーションが他にあるのかなどもわかります。

Bean定義のインスタンスを利用する

 Bean定義したクラスのインスタンスにアクセスする際には、@Autowiredというアノテーションを利用します。また、そのサンプルコードがリスト5です。

[リスト5]@Autowiredの利用例(java/src/main/java/com/coltware/contacts/controller/ContactController.javaの抜粋)
@RestController  // (1)Bean定義である指定
@Scope("request")
@RequestMapping("/contact")
public class ContactController {
    @Autowired  //  (2)Bean定義されたオブジェクトを利用する
    ContactRepository contactRepository;
    // : 省略
}

 (1)は、先ほど紹介したBean定義をしているアノテーションです。(2)のように@Autowiredの指定をすればどのクラスでも利用できるわけではなく、必ず、利用する際にはBean定義されているクラス内でのみ利用できます。

 従って、自分でnewによって作成したオブジェクトの場合には自分で設定しなければいけません。

設定ファイルを理解する

 ある程度大きなアプリケーションを作成していると、必ずと言ってよいほど外部の設定から値を読む必要が発生します。

 また、Spring Bootでは既存のミドルウェアの設定の変更がBean定義からも可能ですが、環境に依存する部分などは設定ファイルからも変更できるようになっています。

 しかし、Spring Bootを使う上でアノテーションと同様にどのような設定項目があるのか知ることもなかなか難しいです。

 今回、サンプルアプリケーションのために用意したリスト6の内容から設定がどのように行われているのかを理解していきましょう。

 また、設定ファイルはデフォルトではクラスパス上のapplication.propertiesファイルからデータを取得します。

 ただし、それ以外にもさまざまな方法が用意されていますので、より詳しく知りたい場合には、リファレンスを参考にしてください。

[リスト6]設定ファイルの内容
# サンプルアプリケーション内のコードで利用している設定
app.web.prefix = /web
app.web.resource = classpath:/static/

# httpのサーバポートの指定
server.port = 8081

# JDBCの設定
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:contacts
spring.datasource.username=h2user
spring.datasource.password=

# (省略)

設定ファイルの値を個別にコード内の変数にマッピングする

 設定ファイルの値をコード内で変数として利用する場合には、リスト7のように@Valueアノテーションを利用します。

[リスト7]設定ファイルの内容(java/src/main/java/com/coltware/contact/configuration/WebConfiguration.javaの抜粋)
@Configuration
public class WebConfiguration implements WebMvcConfigurer {

    @Value("${app.web.prefix}") // (1)基本の利用方法
    private String webPrefix;

    @Value("${app.web.resource:classpath:/static/}") // (2)デフォルト値を指定した設定方法
    private String webResource;

    // (省略)
}

 (1)が最も基本的なプロパティ設定値を利用する方法です。ただし、この場合に設定ファイルに指定のキーがない場合には、エラーになってしまいます。

 その場合には、(2)のようにコロン(:)を使ってデフォルト値を設定すれば指定がない場合にも対応が可能です。

 また、@Valueはフィールド以外にもメソッドにも利用できるので、設定を加工して利用したい場合などはsetterメソッドを用意して利用すればよいでしょう。

設定グループとして値を管理する

 JDBCの設定のように設定項目が多くなると設定を1つの塊として管理したくなります。その場合には、@ConfigurationPropertiesを利用します。

 @Valueを多く利用するとどこでどのような設定を使っているのかの管理がすぐに難しくなってしまうため、利用する場合にも限定した利用が望ましいと思います。

 実際、Spring Bootのソースを見てもほとんど@Valueは使われていません。

 例えば、server.portの設定を管理しているコードであるSpring Boot内のServerPropertiesクラスのソースを見てみます。

[リスト8]https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
    // : 省略
    public void setPort(Integer port) {
        this.port = port;
    }
    // : 省略
}

 また、同様にspring.datasourceの管理をしているクラスであるDataSourcePropertiesクラスのソースを見て見ます。

[リスト9]https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
    // : 省略
    public void setDriverClassName(String driverClassName) {  // (1)spring.datasource.driver-class-nameの設定
        this.driverClassName = driverClassName;
    }
    // : 省略
    public void setUrl(String url) { // (2)spring.datasource.urlの設定
        this.url = url;
    }
    // : 省略
}

 どちらにも、@ConfigurationPropertiesという指定があり、また、prefixで設定グループ名が指定されています。例えば(1)は、spring.datasource.driver-class-nameの指定であり、大文字を“-(ハイフン)”に置き換えて読み替えることができます。

 また、(2)ではspring.datasource.urlの設定であり、どちらもprefixで指定したグループ以下の名称はsetterメソッドを使って設定が行われます。

 こういったルールで設定できる項目がわかるので、他の設定項目もどこのソースで設定が行われているかを調べると、その設定の意味などを把握する上で参考となります。

 設定項目もアノテーションと同様に網羅的な説明があるわけではありませんが、よく使われる設定などは多くの方が疑問に思うため、ブログなどにアップされている情報も多いでしょう。基本的にはそれらが参考になります。

 ただし、使っているSpring Bootのバージョンによって多少変わってしまったり、設定が増えていたりといったこともまれにあるため、筆者も疑問に思ったり、具体的に他の設定はないのかを調べる際には、上記のようにソースコードを調べてしまいます。

最後に

 Spring Bootを使っていると、自分が実装したいソースコードの内容以外に、どういったアノテーションを使えばよいのか、または、どういった設定をすれば希望する内容が有効(無効)になるのかをよく調べることになります。

 しかし、これらすべてを理解するとなると非常に大変で、また、Spring Bootの簡単に始められるといった利点をも失ってしまいます。従って、アノテーションや設定を使いこなすことを気にせずプログラムを始めるのが、Spring Bootでは大切なことだと筆者は思っています。

 ただし、問題にぶつかった時にSpring Bootの仕組みがわからないと、どこで何がおきているのかがわからないため、今回紹介した最低限の仕組みについての理解は必要だと思います。

 次回は、サンプルコードのWeb APIを作成する際に必要なアノテーションやデータベース関連のアノテーションなど機能部分について紹介します。

参考資料