最終更新日:2020/04/04 原本2018-03-12

Spark Web Framework の使い方 : Getting Start 編

今回から何回かに分けて、Java界隈で人気らしい「Spark Framework」の使い方について書いていきたいと思います。

Spark Frameworkの公式ページはこちら -> Spark Framework: An expressive web framework for Kotlin and Java

SparkやマイクロWebサービスがどんなものかとかは、公式ページやQiitaの他の方の記事におまかせして、ここでは私なりのSparkの使い方を解説していきます。

今回使用するライブラリ

  • spark-core 2.7.1
  • spark-debug-tools 0.5

Hello World!

実装

では、さっそくSparkの使い方を見ていきましょう。
まずは公式アナウンスのとおりPOMファイルを記述します。

  • pom.xml
<properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
    <dependency>
        <groupId>com.sparkjava</groupId>
        <artifactId>spark-core</artifactId>
        <version>2.7.1</version>
    </dependency>
</dependencies>

続いて、アプリのエントリーポイントを作成します。

  • Application.java
import static spark.Spark.get;

public class Application {

    public static void main(String[] args) { 
        // getメソッドの第一引数にはリクエストをマッピングするパス、第二引数にはリクエストを処理するハンドラを記述します。
        get("/", (req, res) -> "hello world!");
    }

}

ハンドラはRouteインターフェイスを実装して作成しますが、@FunctionalInterfaceなのでJava 8以降なら例のようにラムダ式で実装することができます。寄り道してRouteインターフェイスのシグネチャを見てみましょう。

@FunctionalInterface
public interface Route {

    Object handle(Request request, Response response) throws Exception;

}

Routeインターフェイスはリクエストとレスポンスを引数にとり、レスポンスボディを返却するよう実装します。

ここでいうリクエストとレスポンスはSpark独自の型で、HttpServletRequestなんかとは異なります。

実行

さて、実装したアプリを起動してみましょう。
私はEclipseで実装しているので、「実行」->「Javaアプリケーション」->「作成したクラスのMainメソッド」と選択していきます。

実際の起動時にjavaコマンドでMainメソッドを指定するのはナンセンスですね。

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.

実行すると、コンソールに上記のようなログが出力されると思いますが、ちゃんと起動しています。

SparkはロギングをSLF4Jで実施していますが、今回のアプリではロギング実装ライブラリを入れていないので、警告が出ています。

実行したアプリにCURLでアクセスしてみましょう。もちろんブラウザでURLを叩いてもOKです。

$ curl -s http://localhost:4567
hello world!

なんとも味気ないですが、「Hello World!」が出力されました。

Sparkアプリのデバッグ

Eclipseでデバッグするから問題ないぜ!という方もいると思いますが、
Sparkでは公式でデバッグツールが提供されています。

spark-debug-toolsは、以下のように簡単に適用できます。

  • pom.xml
<dependency>
    <groupId>com.sparkjava</groupId>
    <artifactId>spark-debug-tools</artifactId>
    <version>0.5</version>
</dependency>
  • Application.java
import static spark.Spark.get;
import static spark.debug.DebugScreen.enableDebugScreen;

public class Application {

    public static void main(String[] args) { 
        // (1) `enableDebugScreen`メソッドでデバッグ画面が有効になります。
        enableDebugScreen();

        // (2) ハンドラで例外(`Exception`)がスローされると、自動的にデバッグ画面に遷移するようになります。
        get("/", (req, res) -> { throw new Exception("error"); });
    }

}

デバッグ画面はFree Markerで実装されているようです。たまにデバッグ画面のレンダリングに失敗することもあるので、Free Markerの知識があると使いやすいと思います。

Sparkプロジェクトテンプレートの作成

ここまでの動作確認を踏まえて、Sparkアプリのプロジェクトテンプレートを作成しておきます。

  • pom.xml
<properties>
    <!-- (1) Settings -->
    <java.version>1.8</java.version>
    <encoding>UTF-8</encoding>
    <spark.mainClass>io.github.yoshikawaa.spark.sample.Application</spark.mainClass>
    <!-- (2) Maven Properties -->
    <maven.compiler.source>${java.version}</maven.compiler.source>
    <maven.compiler.target>${java.version}</maven.compiler.target>
    <project.build.sourceEncoding>${encoding}</project.build.sourceEncoding>
    <project.reporting.outputEncoding>${encoding}</project.reporting.outputEncoding>
    <exec.mainClass>${spark.mainClass}</exec.mainClass>
</properties>

<build>
    <!-- (3) Create Fat Jar -->
    <plugins>
        <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>3.1.0</version>
            <configuration>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
                <archive>
                    <manifest>
                        <mainClass>${spark.mainClass}</mainClass>
                    </manifest>
                </archive>
            </configuration>
            <executions>
                <execution>
                    <id>make-assembly</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

<dependencies>
    <!-- (4) Spark -->
    <dependency>
        <groupId>com.sparkjava</groupId>
        <artifactId>spark-core</artifactId>
        <version>2.7.1</version>
    </dependency>
    <dependency>
        <groupId>com.sparkjava</groupId>
        <artifactId>spark-debug-tools</artifactId>
        <version>0.5</version>
    </dependency>

    <!-- (5) Logging -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>1.7.25</version>
    </dependency>

    <!-- (6) Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.20</version>
        <scope>provided</scope>
    </dependency>
</dependencies>
no. description
(1) プロジェクトのセッティングを集約定義します。基本的に、こことdependenciesだけ変えればそれなりにアプリが作れることを意識します。
(2) Mavenプラグインにセットするプロパティを定義します。
* maven.compiler.source, maven.compiler.targetは最初に出てきた、Java 8コンパイルするための定義です。
* project.build.sourceEncoding, project.reporting.outputEncodingはソースやJavaDocをエンコードするための定義で、Eclipseでの文字化け防止に役立ちます。
* exec.mainClassはExec Maven Pluginで実行するときのMainクラスを指定するための定義で、Mavenからアプリを実行するときに使います。
(3) パッケージングしたJarを実行するときにjavaコマンドでMainクラスを指定しなければならない!、実行してみたけど依存ライブラリが足りない!などの問題をクリアするため、Maven Assembly Pluginで依存関係も含めてまとめたJarを作成します。
(4) spark-corespark-debug-toolsへの依存関係を定義します。
(5) デフォルトロギング実装としてslf4j-simpleへの依存関係を定義します。ロギングについては、次回以降解説します。
(6) 今回触れませんでしたが、コーディングサポートのためlombokへの依存関係を定義します。
  • Application.java
import static spark.Spark.get;
import static spark.debug.DebugScreen.enableDebugScreen;

public class Application {

    public static void main(String[] args) { 
        // configure application.
        enableDebugScreen();

        // configure routes.
        get("/", (req, res) -> "hello world!");
    }

}

まとめ

Sparkはスタートアップまでが非常に早いので、簡単に動くアプリをさっと作りたいときには重宝しそうです。
とはいえ、それなりにまともなアプリを作ろうと思うと、自分で考えなきゃいけないところがあり、Spring Bootのように「あまり仕組みを知らなくてもフルセットのアプリが作れる」という簡単さはないところですね。

次回からは、テーマを決めて実装方法を解説していきたいと思います。