mmts1007’s diary

プログラミング関連の技術系ブログです。

Java Spring Boot その5

前回の続きで POST, PUT, DELETE メソッドを作成をします。

ソース

POST, PUT, DELETE を追加したソースは下記の通りです。
また、モックデータの作成方法を変更しました。

package hello;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.IntStream;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("api/tasks")
@EnableAutoConfiguration
public class RestSampleController {

    static class Task {
        public Task() {
        }

        public Task(Integer id, String context) {
            this.id = id;
            this.context = context;
        }

        public Integer id;
        public String context;
    }

    static Map<Integer, Task> tasks = new HashMap<>();

    @RequestMapping(method = RequestMethod.GET)
    List<Task> getTasks() {
        // GET api/tasks で実行されるメソッド
        return new ArrayList<>(tasks.values());
    }

    @RequestMapping(value = "{id}", method = RequestMethod.GET)
    Task getTask(@PathVariable Integer id) {
        // GET api/tasks/{id} で実行されるメソッド
        return tasks.get(id);
    }

    @RequestMapping(method = RequestMethod.POST)
    @ResponseStatus(HttpStatus.CREATED)
    Task createTask(@RequestBody Task task) {
        // POST api/tasks で実行されるメソッド
        int nextIndex = tasks.keySet().stream().max(Comparator.naturalOrder()).get() + 1;
        task.id = nextIndex;
        tasks.put(nextIndex, task);
        return task;
    }

    @RequestMapping(value = "{id}", method = RequestMethod.PUT)
    Task updateTask(@PathVariable Integer id, @RequestBody Task task) {
        // PUT api/tasks/{id} で実行されるメソッド
        Task targetTask = tasks.get(id);
        if (targetTask == null) {
            return null;
        }
        targetTask.context = task.context;
        tasks.put(id, targetTask);
        return targetTask;
    }

    @RequestMapping(value = "{id}", method = RequestMethod.DELETE)
    @ResponseStatus(HttpStatus.NO_CONTENT)
    void deleteTask(@PathVariable Integer id) {
        // DELETE api/tasks で実行されるメソッド
        tasks.remove(id);
    }

    public static void main(String[] args) {
        // モックデータ作成
        IntStream.range(0, 3).forEach(index -> tasks.put(index, new Task(index, String.format("sample%d", index))));
        SpringApplication.run(RestSampleController.class, args);
    }
}

解説

前回解説した通り、@RequestMapping で POST, PUT, DELETE 各メソッドマッピングする Javaメソッドを作成しました。

@RequestMapping

今回 @RequestMapping(value = "{id}", method = RequestMethod.GET) のように value 属性が登場しました。
value は相対パスを指定することができます。なので getTask メソッドは GET "api/tasks/{id}" とマッピングされます。 また value に指定した {id} はメソッドの引数で取得することができます。
取得するためには @PathVariable アノテーションを付与した 変数を定義します。

@ResponseStatus

ResponseStatus アノテーションは名前の通りですが、レスポンスのステータスを指定することができます。
アノテーションに指定できる値は HttpStatus 型の値です。
HttpStatus は enum となっているので、HttpStatus.CREATED など視覚的にわかりやすい形で指定できます。

@RequestBody

メソッドの引数に指定されている RequestBody アノテーションですが、
こちらも名前の通りリクエストボディの値をマッピングするためのアノテーションです。
今回、リクエストボディに設定される JSON を Task 型の値にマッピングするために使用しています。

結果

メソッドを実行した結果をまとめました。

GET api/tasks

$ curl -v http://localhost:8080/api/tasks -X GET
*   Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> GET /api/tasks HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Sat, 10 Oct 2015 23:40:31 GMT
<
* Connection #0 to host localhost left intact
[{"id":0,"context":"sample0"},{"id":1,"context":"sample1"},{"id":2,"context":"sample2"}]

全てのタスクが返却されています。

GET api/tasks/{id}

$ curl -v http://localhost:8080/api/tasks/2 -X GET
*   Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> GET /api/tasks/2 HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Sat, 10 Oct 2015 23:41:31 GMT
<
* Connection #0 to host localhost left intact
{"id":2,"context":"sample2"}

id で指定したデータが返却されています。

POST api/tasks

$ curl -v http://localhost:8080/api/tasks -X POST -H "Content-Type: application/json" -d "{\"context\" : \"create\"}"
*   Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> POST /api/tasks HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.43.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 22
>
* upload completely sent off: 22 out of 22 bytes
< HTTP/1.1 201 Created
< Server: Apache-Coyote/1.1
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Sat, 10 Oct 2015 23:42:14 GMT
<
* Connection #0 to host localhost left intact
{"id":3,"context":"create"}

作成したデータが返却されています。
@ResponseStatus(HttpStatus.CREATED) を指定したのでレスポンスステータスが HTTP/1.1 201 Created に変化しています。

PUT api/tasks/{id}

$ curl -v http://localhost:8080/api/tasks/1 -X PUT -H "Content-Type: application/json" -d "{\"context\" : \"update\"}"
*   Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> PUT /api/tasks/1 HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.43.0
> Accept: */*
> Content-Type: application/json
> Content-Length: 22
>
* upload completely sent off: 22 out of 22 bytes
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Sat, 10 Oct 2015 23:42:40 GMT
<
* Connection #0 to host localhost left intact
{"id":1,"context":"update"}

id で指定した要素のデータが更新されています。

DELETE api/tasks/{id}

$ curl -v http://localhost:8080/api/tasks/1 -X DELETE
*   Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> DELETE /api/tasks/1 HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 204 No Content
< Server: Apache-Coyote/1.1
< Date: Sat, 10 Oct 2015 23:42:56 GMT
<
* Connection #0 to host localhost left intact

@ResponseStatus(HttpStatus.NO_CONTENT) を指定したので HTTP/1.1 204 No Content となり レスポンスもありません。

以上で REST API の作成は完了です。
次回はモック部分のデータを DB とやりとりするよう修正したいと思います。

Java Spring Boot その4

今回は Spring Boot を使って REST API を作ろうと思います。

DB を使わず、モックを使用します。
次回以降、モックの実装を DB を使った実装に修正しようと思います。

一覧取得

まずは一覧を取得するソースのみ

package hello;

import java.util.ArrayList;
import java.util.List;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("api/tasks")
@EnableAutoConfiguration
public class RestSampleController {

    static class Task {
        Task(String title, String context) {
            this.title = title;
            this.context = context;
        }

        public String title;
        public String context;
    }

    @RequestMapping(method = RequestMethod.GET)
    List<Task> getTasks() {
        List<Task> tasks = new ArrayList<>();
        tasks.add(new Task("sample1", "sample1"));
        tasks.add(new Task("sample2", "sample2"));
        tasks.add(new Task("sample3", "sample3"));

        return tasks;
    }

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

http://localhost:8080/api/tasks にアクセスすると

[
    {
        "title": "sample1",
        "context": "sample1"
    },
    {
        "title": "sample2",
        "context": "sample2"
    },
    {
        "title": "sample3",
        "context": "sample3"
    }
]

上記内容が返ってくると思います。
※上記 JSON は整形しています

解説

@RequestMapping("api/tasks") をコントローラクラスに付与することで、このクラスが "api/tasks" とマッピングされます。

@RequestMapping(method = RequestMethod.GET) を getTasks メソッドに付与することで GET "api/tasks" が getTasks メソッドマッピングされます。

同様に POST, PUT, DELETE をマッピングするメソッドを作成すれば、REST API の作成は完了です。
今回はここまで。

Java Spring Boot その3

今回は Spring Loaded について

Spring Loaded とは

Spring Loaded とは Seasar でいう Hot Deploy を可能にするライブラリです。 アプリケーションを再起動することなく、ソースの変更を反映することができます。 これにより生産性を向上することができます。

使い方

  1. Spring Loaded をダウンロード github.com

  2. アプリケーション実行時に JVM パラメータに Spring Loaded を指定

java -javaagent:<pathTo>/springloaded-{VERSION}.jar -noverify SomeJavaClass

サンプル

今回私が試した Eclipse での実行方法をサンプルとして載せます

  1. Spring Loaded をダウンロード

適当な場所にダウンロード

/path/to/spirng_loaded/springloaded-1.2.4.RELEASE.jar
  1. Eclipse でアプリケーション実行時に JVM パラメータに Spring Loaded を指定

Run as -> Run Configurations...

VM arguments に

-javaagent:/path/to/spirng_loaded/springloaded-1.2.4.RELEASE.jar -noverify

を指定し、実行

これだけで、変更が即時反映されるようになる

補足

本来は Gradle に依存ライブラリ、JVM パラメータを記載し、gradle bootRun で Spring Loaded が動くようにしたかった。 Gradle x Spring Loaded x IntelliJ の記事は見つかるが、Eclipse の記事が見つからず、今の所できていない。 一旦動く状態にしたかったため、上記設定で行っている状態である。 解決次第改めて記事を書こうと思う。

Java Spring Boot その2

今回は 前回の記事 の解説を簡単にしようと思います。

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@EnableAutoConfiguration
public class SampleController {

    @RequestMapping("/")
    @ResponseBody
    String home() {
        return "Hello Spring Boot!";
    }

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

@Controller

コントローラを表すアノテーション

@EnableAutoConfiguration

Spring Boot の重要なアノテーション。このアノテーション 1 つで色々な設定を自動で行ってくれる。
XML 等の設定ファイル不要で様々な設定をしてくれるところが有難い。

@RequestMapping

リクエストをメソッドマッピングするためのアノテーション
上記例でいうと "/" というリクエストあった場合、 home メソッドが呼ばれる。

@ResponseBody

Web のレスポンスボディに値をバインドするためのアノテーション

今回は簡単にこんなところで。

Java Spring Boot

今回は Spring Boot の記事を書こうと思います。
開発メモという形で何回かに分けて上げていこうと思います。

まずは Spring Boot のセットアップから

開発環境

セットアップ

Gradle ファイルの作成、Java プロジェクトのディレクトリ構成作成

$ mkdir hello_boot
$ cd hello_boot
$ gradle init --type java-library

# 不要なファイル削除
$ rm src/main/java/Library.java
$ rm src/test/java/LibraryTest.java

Spring Boot 用の設定に修正

参考: http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#getting-started-gradle-installation

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.2.6.RELEASE'
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'spring-boot'

jar {
    baseName = 'hello_boot'
    version =  '0.0.1-SNAPSHOT'
}

repositories {
    jcenter()
}

dependencies {
    compile 'org.springframework.boot:spring-boot-starter-web'
    testCompile 'org.springframework.boot:spring-boot-starter-test'
}

Eclipse 設定ファイルを作成

$ gradle eclipse

サンプルアプリケーションを作成

参考:http://projects.spring.io/spring-boot/

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@EnableAutoConfiguration
public class SampleController {

    @RequestMapping("/")
    @ResponseBody
    String home() {
        return "Hello Spring Boot!";
    }

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

サンプルアプリケーションを実行

$ gradle run

http://localhost:8080/ にアクセスして、Hello Spring Boot! と表示されれば OK!

Java Spark

ブログを書く時間をうまく確保できてない mmts1007 です。

今回は Javaフレームワーク Spark について書きます。

Java Spark とは

Spark は Java の Web アプリケーションフレームワークです。
Rubyフレームワーク Sinatra にインスパイアされたフレームワークです。
Java 8 で追加になった ラムダ式 を使うことでシンプルに記述することができます。

特徴

特徴としては前述のとおり

です。

サンプル

Hello Spark

とてもシンプルな例です

gistb99122ff8af2fa0774e4

ルーティング

Sinatra は HTTP メソッドと URLを元にルーティングを決定します。
Spark は Sinatra にインスパイアされたため、Spark も同様にHTTP メソッドと URLを元にルーティングを決定します。
上記の例だと HTTP メソッド:GET、URL:"/" にリクエストした場合、上記のメソッドが実行されることになります。

シンプルな処理内容の記述

Spark はラムダ式を使用することで、シンプルに記述することができます。
get("URLパス", "処理内容"); となっており、第 2 引数にラムダ式を渡すことができます。
これにより、実際の処理内容をシンプルに記述することが可能となりました。

組み込みサーバ

今までの Web アプリケーションは war ファイルを作成し、それを Tomcat などのアプリケーションサーバにデプロイするのが主流でしたが今は組み込みサーバが流行っています。
サーバが組み込まれていると java -jar xxxx.jar のように Java コマンドで実行するだけでサーバが起動します。
アプリケーションサーバを用意しなくとも Web アプリケーションが実行できるのはとても楽です。

Rest API

REST API のサンプルとしてタスクの取得/登録/更新/削除のソースの書き方のイメージです。

public class TaskSpark {
    public static void main(String[] args) {
        get("/tasks", (req, res) -> {
            // 1. タスク内容を全て戻す
        });

        get("/tasks/:id", (req, res) -> {
            // 2. id に指定されたタスクを戻す
        });

        post("/tasks", (req, res) -> {
            // 3. パラメータに指定された内容でタスクを作成する
        });

        put("/tasks/:id", (req, res) -> {
            // 4. パラメータに指定された内容で id のタスクを更新する
        });

        delete("/tasks/:id", (req, res) -> {
            // 5. id に指定されたタスクを削除する
        });
    }
}

Sinatra ライクな記述のため、どのようなルーティングなのか分かりやすいです。
get の /tasks のリクエストがきたら 1. が実行される といった形です。
上記のソースのように REST API のような HTTP のメソッドに意味を持たせる場合、分かりやすいフレームワークだと思います。

実際に処理を書いたサンプルが下記のとおりです。
※ DB 処理を書くのが面倒だったので、適当にメモリに保持。ここら辺の記述は適当ですw

gist247abdc95d81b72bbfaa

new JsonTransformer() を 引数に与えることで、戻りの型を変換することができます。これは Spark の機能です。
今回は JSON 型に変換したかったので JsonTransformer クラスを作成し、指定しています。

まとめ

Sinatra ライクな Spark についての紹介でした。

といった Spark の特徴が少しでも分かって頂ければと思います。

Seasar サポート終了

2日に1回書くのがこんなに辛いとは…w もう少し頑張りますw

2015.09.26 に Seasar Conference 2015 が開催されました。

Seasar Conference 2015 | September 26, 2015 @ Hosei University

その中で、Seasar サポート終了 が発表されました。

Seasar 2 を使った開発を 2年位経験していた私にとっては、ビックニュースでした。
本気で次のフレームワークを探さなきゃなと思いました。

と言うことで、移行先に考えているフレームワークを紹介していこうと思います。
(あくまで私の好み、興味です。)

Spring Boot

http://projects.spring.io/spring-boot/

去年の JJUG CCC で知ったフレームワークです。 そこから興味を持ち、JJUG Night Seminer 等に参加しました。

【東京】JJUG ナイト・セミナー「中上級者向け!Spring Bootハンズオン!」3/25(水)開催 | 日本Javaユーザーグループ

本も持ってます!(著者のサイン入り!)

http://www.amazon.co.jp/%E3%81%AF%E3%81%98%E3%82%81%E3%81%A6%E3%81%AESpring-Boot%E2%80%95%E3%80%8CSpring-Framework%E3%80%8D%E3%81%A7%E7%B0%A1%E5%8D%98Java%E3%82%A2%E3%83%97%E3%83%AA%E9%96%8B%E7%99%BA-I%E3%83%BBO-BOOKS/dp/4777518655www.amazon.co.jp

Spark

sparkjava.com

Sinatra Like なフレームワークです。
Sinatra が好きな私にとっては嬉しいフレームワークでした。

HTTP のメソッドJavaメソッドマッピングされているので、
Resutful API を作るときにはわかりやすいと思います。

LastaFlute

LastaFlute (Javaでリーンスタートアップ) | DBFlute

speakerdeck.com

Seasar Conference 2015 で発表されたフレームワークです。
SeasarJava 8 でフォークしたフレームワークとのことです。
( LastaFlute 早く触りたい。早く週末になれ!※まだ月曜日 )

それぞれのフレームワークの詳細については個別に書いていこうと思います。
次の記述でまずは Spark について書こうと思います。