エンタープライズギークス (Enterprise Geeks)

企業システムの企画・開発に携わる技術者集団のブログです。開発言語やフレームワークなどアプリケーション開発に関する各種情報を発信しています。ウルシステムズのエンジニア有志が運営しています。

Spring特別勉強会レポート(前編)

2015年9月2日、Spring FrameworkコントリビュータのJosh Long氏がウルシステムズを電撃訪問し、プライベートセッションを開催するというサプライズがありました。日本Springユーザー会のフォーラムに合わせて来日した折、過密なスケジュールのすきまを縫って勝どきまで足を運んでくれたのです。

セッションのタイトルは「Bootiful Application」。Spring Frameworkの新機能である「Spring Boot」と「Spring Cloud」の概要を解説するものです。「百聞は一見にしかず」とばかりに、息もつかせぬ超高速コーディングで次々とデモを披露するLong氏に参加者一同、圧倒されっぱなし。刺激が多く、実りの多い2時間でした。今回はプライベートセッションのエッセンスをまとめてご紹介します。

f:id:enterprisegeeks:20150929150136j:plain

Josh Long氏。ユーモアセンス抜群のギークです。

f:id:enterprisegeeks:20150929150443j:plain 瞬きする間にコードが書き換わっている恐ろしいコーディングスピード。彼のマネをしてIDEIntelliJ」を購入しましたが、同じようにはできませんでした。

セッションのテーマ

今回のセッションのテーマは以下の3つです。

  • マイクロサービス
  • Spring Bootの概要
  • Spring Cloudの概要

この前編では最初の2つについて紹介します。

マイクロサービス

マイクロサービスアーキテクチャとは、独立した複数のサービス群とそれらのサービス間をAPIで協調させて構成することを指します。 対極にあるのは、モノシリックアーキテクチャ。いわゆる一枚岩の構成です。複数のサービスを同一サーバに同居させたり、コードやライブラリを共有したりするアーキテクチャを指します。企業システムなどがまさにそれです。

マイクロサービスはモノシリックの課題を解決するために登場した概念です。 Long氏はモノシリックアーキテクチャには規模が大きくなるにつれて以下のような問題が発生すると説明しました。

  • 言語やフレームワークが固定されるためシステムの一部をアップデートすることが困難になる
  • コードやライブラリを共有しているため変更時の影響が大きく、開発スピードが下がる
  • どこかでパフォーマンス上の問題や障害が発生すると影響がシステム全体に波及することがある

マイクロサービスは上記の問題を解消するためにサービス間の独立性を高めています。 具体的には、サービス間の情報のやりとりをRESTなどのAPIに限定しています。APIを変更しなければサービスの実装言語などは自由に選択できます。サービスごとの変更も容易です。1つひとつのサービスの規模を小さくできるため、必然的にコードはシンプルになり、開発効率やメンテナンス性が向上します。もちろん、サービスを複数のサーバに分散してデプロイすることも可能です。クラウド時代に合ったシステムアーキテクチャといえるでしょう。

Spring Boot

Spring BootはSpring の機能を簡単に使えて起動も楽な、単一のアプリ(=サービス)を作るためのフレームワークです。 わずかな設定をするだけでデータベースアクセスやWebアプリの作成できます。 そのため、マイクロサービスアーキテクチャを構成する1つ1つのサービスを作成するのに適しているとの説明がありました。

Spring Bootの特徴は大きく以下のとおりです。

  1. Spring の各種プロダクトを依存性としてpom.xmlなどに記述して使用可能にする
  2. Tomcatなどのアプリケーションサーバを内部に組み込み、Jarファイル単体でアプリケーションを起動可能

以下、それぞれ解説します。

1. Spring の各種プロダクトを依存性としてpom.xmlなどに記述して使用可能にする

Spring は DIコンテナAOPを中核に、さまざまなプロダクトが存在します。このため、それらの組み合わせや依存するライブラリの管理が大変です。Spring boot では使用したいプロダクトを選択形式でpom.xmlに記述すればよいため、ライブラリ管理の手間が軽減されます。

以下は、Spring Boot形式のプロジェクト雛形を生成できるWebページのスナップショットです。

http://start.spring.io/

f:id:enterprisegeeks:20150924183238p:plain

プロジェクト名などのプロパティと使用するライブラリを選択して生成を行うと、zipファイルでSpring Bootのプロジェクトを自動生成します。例えば、Web向けのプロダクトの Web, データアクセスに JPA, データベースとして H2 を選択すると、プロジェクトのpom.xmlには以下のように「spring-boot-starter」で始まる依存性が記述されるといった具合です。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

Spring プロダクトが簡単に使用可能になっているのがわかるかと思います。これだけの記述で、Spring MVC と Spring Data JPA, および H2 データベースのライブラリが全て整備されます。開発途中で使用するプロダクトに変更や追加があった場合もpom.xmlの依存性を修正することで対応できます。

なお、Spring Bootのプロジェクトは、http://start.spring.io/だけでなくmvn archetypeやSpring Tool Suite、IDEA などの IDE でも作成できます。

実際、Long 氏は IDEA によるSpring boot プロジェクトの作成も実演しました。 余談ですが、 Long 氏は IDEAの愛好者であるようでした。

また、今回はMavenのpom.xmlによるサンプルを掲載していますが、Gradleでも依存性管理を行えます。

 2. Tomcatなどのアプリケーションサーバを内部に組み込み、Jarファイル単体でアプリケーションを起動可能

Spring Boot のプロジェクトには、以下のような mainメソッドを持ったアプリケーションクラスが生成されます。

@SpringBootApplication
@EnableWebMvc // Spring MVC を有効にするアノテーションを追加
public class DemoApplication {

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

このアプリケーションクラスは Spring Boot アプリケーションのエントリポイントになるほか、各種設定や初期処理なども記述できます。上記の例では、Spring MVC を有効にするため、@EnableWebMvc を追加しています。冗長な XML ではなく、アプリケーションクラスにアノテーションを付与することで設定を有効にすることができます。

mainメソッドがあることからわかるとおり、Spring Bootアプリケーションは、Javaコマンドから起動します。開発時はIDEなどからmainメソッドを実行するとアプリケーションが起動します。また、ビルドを行うと、作成したクラスファイルや依存性で記述したJarファイル、HTMLなどのリソースが全て1つのJarファイルとして生成されます。Tomcat などのアプリケーションサーバもJarの中に組み込まれますので、Jarファイル単体でWebアプリケーションが起動できます。

よって、アプリケーションが動作する環境もJavaのインストールさえされていればよく、インフラの準備も簡略化されます。 PaaS のようなクラウド環境での動作も簡単であると述べていました。

上記2点の特徴により、準備作業、開発、運用の全ての面で効率化を実現しているのです。 それでは、いくつかのプロダクトの紹介を行っていきます。

Spring MVC

Spring MVC は REST形式で、XML,JSONなどのデータとテンプレートを使用したHTMLの両方に対応したMVCフレームワークです。主要な成果物は、コントローラおよびViewです。

まずは、JSONなどのデータをやりとりするRESTコントローラです。

@RestController
public class RestHelloWorldController {

    private final AtomicLong counter = new AtomicLong();

    @RequestMapping(value="/hello", method=RequestMethod.GET)
    public Greeting sayHello(
            @RequestParam(value="name", required=false,
              defaultValue="Stranger") String name) {
        return new Greeting(counter.incrementAndGet(), "Hello!" + name);
    }
}

public class Greeting {

    private final long id;
    private final String content;

    public Greeting(long id, String content) {
        this.id = id;
        this.content = content;
    }
    // アクセッサは省略。
}

上記のように @RestController を付与したクラスがRESTコントローラになります。メソッドには、@RequestMapping アノテーションでエンドポイントURLとHTTPメソッドを指定し、引数にはリクエストパラメータを指定し、戻り値は通常のJavaクラスを指定します。アプリケーションクラスに @EnableWebMvc を付与して起動すれば、/hello というエンドポイントが有効になります。

http://localhost:8080/hello?name=hoge にアクセスすると、以下のJSON を得ます。

{"id":1,"content":"Hello!hoge"}

これがRESTコントローラの基本です。 設定ファイルも何もなくWebアプリケーションができてしまいました。 この記事ではわかりやすさのため、それぞれのコードをファイルに分割した記述にしていますが、 実際のデモでは、内部クラスを使用して1ファイルだけでアプリケーションを記述していました。

Long氏はTwitterにも投稿可能なくらいに短く(つまり140文字以内に短縮が可能な)アプリケーションが書けるとジョークを交えていました。

続いて、Viewを返すコントローラを見てみます。

@Controller
public class HelloWorldController {
    
    @RequestMapping(value="/greeting", method=RequestMethod.GET)
    public String greeting(@RequestParam(value="name", required=false,
                    defaultValue="World") String name,
            Model model) {
        model.addAttribute("name", name);
        return "greeting";
    }
}

コントローラには、@Controller アノテーションを付与します。メソッドに指定するアノテーションはRESTコントローラと同じです。戻り値には、Viewの名前を指定します。また、引数のModelクラスに属性を設定すると、その属性はViewの中で参照可能になります。

続いて、Viewの定義です。

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Getting Started: Serving Web Content</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
    <p th:text="'Hello, ' + ${name} + '!'" />
</body>
</html>

Viewは、Thymeleaf というテンプレートエンジンを使用します。 Thymeleafは、HTMLタグのthで始まる属性に動的な情報を指定することで、なるべく素のHTMLを保つことを目的にしています。

Long氏はプレーンなHTMLでViewが書けるのはとても良いことだと説明していました。

上記のHTMLは、ブラウザでもそのまま表示できますし、 アプリケーションを起動して、 /greeting にアクセスすると、 pタグのテキスト要素に、 Hello <nameパラメータ> ! が設定されたHTMLが出力されます。

以上が、Spring MVC の説明となります。

Spring Data JPA

Spring でデータベースを扱う方法はいくつかあるのですが、その中からJPAを取り上げます。

エンティティの定義は通常のJPAと同様にJavaクラスに アノテーションを付与することで行います。

@Entity
public class Reservation implements Serializable {

    @Id
    @GeneratedValue
    public Long id;
    
    public String reservationName;
    
}

エンティティクラスを作ると、アプリケーションの起動時に自動でテーブルが作成されます。

Springではデータアクセスを行う部品をリポジトリと呼び、JPA版のリポジトリJpaRepositoryインターフェースを継承して作成します。

// JpaRepositoryの型パラメータは、エンティティクラスとそのキー属性の型
public interface ReservationRepository 
        extends JpaRepository<Reservation, Long>{

    /** where reservationName = :name のクエリが自動生成される */
    public List<Reservation> findByReservationName(String name);
    
}

これだけのコードでデータベースとのやり取りが可能です。実装クラスは不要です。JpaRepositoryには、基本的なCRUD操作や全件取得のメソッドが最初から用意されています。また、上記の findByXXX メソッドのように特定の命名規則を満たすメソッドはその内容からクエリが自動生成されます。アノテーションで任意のクエリを設定する方法も用意されています。

リポジトリクラスは、@Autowired で他のクラスにインジェクションして使用します。

@RestController
public class HogeController {

    @Autowired
    ReservationRepository repo;
    
    @RequestMapping(value="/reservation/{name}", method=RequestMethod.GET)
    public List<Reservation> reservationsByName(
            @PathVariable("name") String name) {
        return repo.findByReservationName(name);
    }
}

REST リソース

続いて、リポジトリの各メソッドを直接 RESTエンドポイントに公開する REST リソースを紹介します。 この機能を使用するには、 spring-boot-starter-data-rest を依存性に追加します。

上で紹介したJPAリポジトリを以下のように修正します。

@RestResource
public interface ReservationRepository 
        extends JpaRepository<Reservation, Long>{

    /** where reservationName = :name のクエリが自動生成される */
    public List<Reservation> findByReservationName(
            @Param("name")String name);
}

修正点は、クラスに @RestResource を付与したことと、メソッド引数に リクエストパラメータに対応させる@Param を付与したことです。

これだけで、アプリケーションにはいくつかのエンドポイントとHTTPメソッドが公開されます。例えば以下のようなものです。

  • /reservations/{id} - エンティティ1件の取得(GET)・更新(PUT)・削除(DELETE)
  • /reservations - エンティティの一覧ページング取得(GET)・新規登録(POST)
  • /reservations/search/findByReservationName?name=xxx - findByReservationNameメソッドの呼び出し(GET)

GET /reservations にアクセスすると以下のような応答を得ることができます。

{
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/reservations{?page,size,sort}",
      "templated" : true
    },
    "next" : {
      "href" : "http://localhost:8080/reservations?page=1&size=20{&sort}",
      "templated" : true
    },
    "search" : {
      "href" : "http://localhost:8080/reservations/search"
    }
  },
  "_embedded" : {
    "reservations" : [ {
      "reservationName" : "name0",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/reservations/379"
        }
      }
    }, {
      "reservationName" : "name1",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/reservations/380"
        }
      }
    } /*省略*/]
  },
  "page" : {
    "size" : 20,
    "totalElements" : 33,
    "totalPages" : 2,
    "number" : 0
  }
}

この応答は、HATEOAS という形式に沿っています。HATEOASはHTTPのハイパーメディアの性質を生かして、応答結果のほかに画面遷移や状態遷移などの情報をリンクとして埋め込んだものです。GET /reservations はデフォルトでは先頭20件の検索結果を返すのですが、次の20件を表示するURLや、検索結果1件分の詳細を表示するURLなどの情報があることがわかるかと思います。

Long氏によればHATEOASを使うことで REST API のアプリケーションが会話し、巨大なアプリケーションが構築できるとのことでした。

JMX, Actuator, Remote Shell

続いて運用監視に関するプロダクトを紹介します。Spring Boot ではアプリケーションの設定や環境情報、ログなどを参照する手段がいくつか用意されています。

JMX

JMXはデフォルトで有効化されています。jconsoleなどのツールから使用でき、環境情報、エンドポイントの一覧、ヘルスチェック、統計(メモリ使用量やGC回数など)、トレースログの参照、アプリケーション停止などが行えます。 f:id:enterprisegeeks:20150925143424p:plain

Actuator

Actuator はJMXで参照可能な情報を REST API で提供するプロダクトです。依存性 spring-boot-actuator を記述するだけで有効になります。

/mappings (エンドポイント一覧), /env(環境情報), /metrics(統計), /trace(ログ) などのエンドポイントが使用可能になります。

また、これらのエンドポイントは設定ファイルでアクセス条件を設定可能ですが、 ロールによる認可制御のような詳細設定を行うなら、Spring Security と組み合わせることが可能だと説明していました。

Remote Shell

Remote Shell は 同様の情報を SSH で取得可能にするプロダクトです。

依存性 spring-boot-starter-remote-shell を記述すると有効になります。

アプリケーションにSSHでアクセス可能になり、各種コマンドを実行して情報を取得することができるようになります。

f:id:enterprisegeeks:20150925144356p:plain

まとめ

以上がマイクロサービスと Spring Boot に関する内容です。

次回は、複数のSpring Boot アプリケーションを連携させる Spring Cloud について紹介します。

[前多 賢太郎][OK]