読者です 読者をやめる 読者になる 読者になる

Java8ベースの Webフレームワークを作ってみた - Java7 EOLを迎えて -

Java7 も本日、EOLを迎えました。 ということで、Java8ベースの Selva という Webフレームワークを作ってみました。

Overview

まずは、最小の コード例

package io.github.chibat.selva.example;

import io.github.chibat.selva.App;
import io.github.chibat.selva.server.Server;

public class FirstApp implements App {
  public void init() {
    resource("/").get(req -> text("Hello, World!")); // (1)

    resource("/users/(.+)") // (2)
    .get(req -> text("Show " + req.pathParam())) // (3)
    .post(req -> text("Update " + req.pathParam())); // (4) 
  }

  public static void main(String[] args) {
    Server server = new Server();
    server.add(FirstApp.class);
    server.listen();
  }
}

このmainメソッドを実行すると、アプリケーションサーバが起動し、Webブラウザが起動し、

Hello, World!

と表示されます。(1)のコードが実行されています。

次に以下のコマンドを実行してみます。 (2)で指定している正規表現でURLのパスが解析され、パスパラメータとして(3)で利用しています。

$ curl -x GET http://localhost:8080/users/chiba
Show chiba

次にHTTPメソッドをPOSTに変更すると(4)のコードが実行されます。

$ curl -x POST http://localhost:8080/users/chiba
Update chiba

一見 Sinatra風なフレームワークに見えるかもしれませんが、素のサーブレットの構成に近いと思っています。 Sinatra風だとリソース(パスパターン)とHTTPメソッドが1対1で結びつきますが、このSelvaは、一つのリソースが複数のHTTPメソッドを持ちます。 この構成の方が、自然かと思ったので。(実はSinatraよく分かってない)

Features

実装した機能を列挙します。

Request に関する処理

  • リクエストとHandlerオブジェクトのルーティング
  • URLパスパターンによる、パスパラメータの取得
  • リクエストパラメータをbeanにマッピング
  • バリデーション(Hibernate validatorに依存)

Response の種類

種類 メソッド 依存
Plain Text text
HTML Template template Thymeleaf
JSON json Jackson
Redirect redirect
Forward forward -

その他

  • フィルタ機能
  • Jetty 組み込みでの起動(executable jarが作れる)
  • クラスパス上の静的ファイルのレスポンス
  • WebJars 連携
  • サーバ起動時にWebブラウザを起動する。

maven レポジトリの利用

今は、Maven Central じゃなく jCenter なんですかね。'Forget Maven Central'とか書かれてる。ということで jCenter にモジュールを登録してみました。

selva-core

selva-core 自体は、特定のサーブレットコンテナに依存しないため、任意のサーブレットコンテナで動作させるパターンで使えます。build.gradle は以下のように書きます。pom.xml の書き方は忘れました。

apply plugin: 'java'
repositories {
  jcenter()
}
dependencies {
  compile 'io.github.chibat:selva-core:0.0.+'
}

selva-jetty

selva-jetty モジュールを使うと、アプリケーション内にJettyを組み込んで起動できます。build.gradle は以下のように書きます。

apply plugin: 'java'
repositories {
  jcenter()
}
dependencies {
  compile 'io.github.chibat:selva-jetty:0.0.+'
}

Example アプリの起動

Download ダウンロードして展開します。

起動

$ gradlew selva-example:run

サーバが起動し、Webブラウザが起動します。

Executable Jar の作成 & 起動

$ gradlew selva-example:jar
$ java -Dfile.encoding=UTF-8 -jar selva-example/build/libs/selva-example-0.0.0.jar

同じく、サーバが起動し、Webブラウザが起動します。Windowsの場合は、ディレクトリセパレタを\で。

TODO

思いつくTODOを。

  • コメント書くw
  • テスト書くw
  • selva-core は、依存ライブラリをなくす。
  • モジュール分割してプラグイン的な仕組みにする。
  • 拡張性が低いのでそこんとこ考える。
  • 国際化機能
  • Tomcat や Undertow 組み込みでの起動
  • DBアクセス機能
  • TypeScript の呼び出し用クライアントコード自動生成機能。(無理そう)

感想

  • 以前だとフレームワーク作るとなるとリフレクションは必須だし、アノテーションは使った方が良いしとなってましたが、Java8 のラムダベースでシンプルなフレームワークが作れるなーと思いました。
  • bintray, jcenter へのモジュール登録を初めてしましたが、予想外に簡単でした。
  • eclipseでチェーンメソッド + ラムダのコードに綺麗にフォーマッタかけることできん。と思いました。
  • Gradle のマルチプロジェクトも初めて使ってみましたが、便利ですね。

以上