Spring と JAX-RSを組み合わせる

キクタローさんから「Javaエンジニア養成読本」という本をいただきました。ありがとうございます。

というわけで、JavaEEがらみ、キクタローさんのエントリに乗っかる形の記事を書いてみました。


実はJavaEE方面のエントリを書くのは今年初めて、去年の夏以来だったりします。かなり間が空きましたね。時間がほしいです。誰か仕事手伝ってください。バイトとかいないかねー。


この間にリリースされたものにSpringFramework 4があります。もうすぐでてから1年で、そろそろ使っても問題ないころ合いだと思います。

また、政治面の問題でSpringを使わなければならないというところもかなり多いと思います。JavaEEの技術を使ってみたいと思ってもフルの機能に乗っかるわけにはいかない状況もあるでしょう。

しかし、JavaEEを構成する要素の中でも、もっとも人気の高いJAX-RSならば組み合わせて利用するのも問題は少ないでしょう。最初のバージョン1.0からかなり実用的でした。そしてSpringは本来のDIコンテナのみに集中してもらうのです。これならば社内のライブラリなどSpringベースの資産も問題なく組み込むことができます。


現在のサーバーサイドJavaではずばりベストの組み合わせは

Spring + JAX-RS

だと思います。政治方面、技術方面とあわせてのバランスが一番取れていて説得しやすい形だと思います。アプリケーションサーバーに左右されませんし、設定によっては軽い開発、スタンドアロンのテストなども可能です。


今回はせっかくなのでSpringBootを使います。JAX-RSの実装はRIのJerseyです。JerseyのスタンドアロンサーバーにSpringを組み込むという方法もあると思いますが、今回はこちらで。どちらにしろ、実際の運用はアプリケーションサーバー上になるでしょうから、開発しやすいものを探すとよいでしょう。

SpringBootのセットアップ

こちらを参照してください。

http://kikutaro777.hatenablog.com/entry/2014/11/20/010828

起動できたら終わり。キクタローさんありがとう(*´▽`*)ノ゛

Jerseyを組み込む

プロジェクトツリーから「依存性」を右クリック、「依存性を追加」を選択します。
以下のダイアログが表示されますので、jersey-spring-3のver2.13を探し、追加します。
http://shinsan.s3.amazonaws.com/diary/2014/1201-01.png

プログラムの構造

いよいよコードを書きます。

今回のプログラムは足し算です。http://localhost:8080/rest/add/12/34とうちこんだら、12+34という計算をするといったものになります。とりあえずレスポンスはtext/plainで。


先に構造書いた方がわかりやすいのでのせますが、すべてのソース構成は以下のようになります。
http://shinsan.s3.amazonaws.com/diary/2014/1201-02.png

上から順に解説します。

logicパッケージ

実際の足し算をするクラスが一つだけあります。足し算くらいならこんな分け方は必要ないのですが、実際はここがデータベースアクセスなどが来るものと思ってください。

restパッケージ

URLへのマッピング、リソース部分です。ここがJAX-RSの領域です。

JerseyConfigクラス

JAX-RSの実装であるJerseyの環境設定部分です。packagesはサブパッケージも検索します。

Mainクラス

SpringBootの設定、起動部分です。コンポーネントスキャンのパスを設定しています。Jerseyサーブレットをrestというパスにマッピングしています。CalcRest#addのパスと併せて「http://localhost:8080/rest/add/a/b」となるわけですね。


見てわかるとおり、全部Javaコードで、設定ファイルはありません。


ソース

CalcLogic.java
package com.mycompany.springboottest.logic;

import org.springframework.stereotype.Component;

@Component
public class CalcLogic {
    public int add(int a, int b){
        return a + b;
    }
}
CalcRest.java
package com.mycompany.springboottest.rest;

import com.mycompany.springboottest.logic.CalcLogic;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/")
public class CalcRest {
    @Inject
    CalcLogic calcLogic;
    
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    @Path("add/{a}/{b}")
    public int add(
            @PathParam("a") int a,
            @PathParam("b") int b){
        
        return calcLogic.add(a, b);
    }
}
JerseyConfig.java
package com.mycompany.springboottest;

import org.glassfish.jersey.filter.LoggingFilter;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.spring.scope.RequestContextFilter;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JerseyConfig extends ResourceConfig{

    public JerseyConfig() {
        register(RequestContextFilter.class);
        register(LoggingFilter.class);
        packages("com.mycompany.springboottest.rest");//リソースクラスの保存先
    }
}
Main.java
package com.mycompany.springboottest;

import org.glassfish.jersey.servlet.ServletContainer;
import org.glassfish.jersey.servlet.ServletProperties;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;

@EnableAutoConfiguration
@ComponentScan(basePackages = {
    "com.mycompany.springboottest.logic"}
)
public class Main {

    @Bean
    public ServletRegistrationBean jerseyServlet() {
        ServletRegistrationBean registration = new ServletRegistrationBean(
                new ServletContainer(),
                "/rest/*"); 
        
        registration.addInitParameter(
                ServletProperties.JAXRS_APPLICATION_CLASS,
                JerseyConfig.class.getName());
        
        return registration;
    }

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

JAX-RSのリソースクラスにはコンポーネントスキャン設定をしなくても見てくれているようなので、省いています。

実行

ブラウザを開き、アドレスバーに http://localhost:8080/rest/add/12/34 を入力します。

http://shinsan.s3.amazonaws.com/diary/2014/1201-03.png


実際にSpringBootで開発する場合ですが、差分デプロイの仕方がわからなかったため毎回起動しているとさすがに遅いです。5,6秒もかかってしまいます。これだとTomcatに普通にデプロイするほうがサクサク開発できそうという感じでした。

というわけで、SpringとJAX-RSの組み合わせのお話でした。さくさく楽々開発しましょう。