Spring BootでRESTfulなサービスを作ってみる

2015.10.14

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

丹内です。 spring bootのドキュメントBuilding a RESTful Web Serviceを参考にして、簡単なアプリケーションを作ってみます。

環境

  • Mac OSX 10.10.5
  • InteliJ IDEA 14.1.5
  • java version "1.7.0_79"
  • Gradle 2.7

サンプルプロジェクトのセットアップ

最初に、サンプルコードをダウンロードします。

$ cd devel
$ git clone https://github.com/spring-guides/gs-rest-service.git
Cloning into 'gs-rest-service'...
remote: Counting objects: 1312, done.
remote: Total 1312 (delta 0), reused 0 (delta 0), pack-reused 1312
Receiving objects: 100% (1312/1312), 673.48 KiB | 322.00 KiB/s, done.
Resolving deltas: 100% (673/673), done.
Checking connectivity... done.

とりあえずドキュメントにそって、gs-rest-service/initialの中身を見てみましょう。

$ cd gs-rest-service/initial
$ ls
build.gradle gradle       gradlew      gradlew.bat  pom.xml      src

次に、InteliJにこのプロジェクトをインポートします。

import_project_1 import_project_3

impot_project_2

以後、InteliJの画面でファイルを作っていきます。

ファイルの作成

まずはsrc/main/java/hello/Greeting.javaです。

package hello;

public class Greeting {

    private final long id;
    private final String content;

    public Greeting(long id, String content) {
        this.id = id;
        this.content = content;
    }

    public long getId() {
        return id;
    }

    public String getContent() {
        return content;
    }
}

このクラスは、今回返したい以下のレスポンスに対応しています。

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

Jackson JSONというライブラリを使っているので、その命名規則に従うようです。
次にsrc/main/java/hello/GreetingController.javaです。

package hello;

import java.util.concurrent.atomic.AtomicLong;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class GreetingController {

    private static final String template = "Hello, %s!";
    private final AtomicLong counter = new AtomicLong();

    @RequestMapping("/greeting")
    public Greeting greeting(@RequestParam(value="name", defaultValue = "World") String name) {
        return new Greeting(counter.incrementAndGet(), String.format(template, name));
    }
}

@RequestMappingでパスとメソッドを結びつけます。
なお、これだとどのHTTPメソッドでも対応します。これをGETだけに限定したい場合は、@RequestMapping(method=GET)とするようです。

最後にsrc/main/java/hello/Application.javaです。

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

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

@SpringBootApplicationアノテーションが、いわゆる「魔法のアノテーション」のようです。

これでOKです。

動かしてみる

今回は、サンプルコードに同梱されているgradlewというスクリプトでビルドします。

$ ./gradlew build
Downloading https://services.gradle.org/distributions/gradle-2.3-bin.zip
...........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
Unzipping /Users/tannaiyuki/.gradle/wrapper/dists/gradle-2.3-bin/5asu2gih68751vffoiv0n84555/gradle-2.3-bin.zip to /Users/tannaiyuki/.gradle/wrapper/dists/gradle-2.3-bin/5asu2gih68751vffoiv0n84555
Set executable permissions for: /Users/tannaiyuki/.gradle/wrapper/dists/gradle-2.3-bin/5asu2gih68751vffoiv0n84555/gradle-2.3/bin/gradle
:compileJava FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':compileJava'.
> 1.8は無効なソース・リリースです

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Total time: 6 mins 23.322 secs

エラーになってしまいました。どうやらjavaのバージョンが設定と合っていないということのようです。
今回は、build.gradleの設定値を変えることで対応します。
これを

sourceCompatibility = 1.8
targetCompatibility = 1.8

こうします。

sourceCompatibility = 1.7
targetCompatibility = 1.7

これでまたビルドしてみます。

$ ./gradlew build
:compileJava
:processResources UP-TO-DATE
:classes
:jar
:findMainClass
:startScripts
:distTar
:distZip
:bootRepackage
:assemble
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE
:check UP-TO-DATE
:build

BUILD SUCCESSFUL

Total time: 11.28 secs

できたようです。実行してみます。

java -jar build/libs/gs-rest-service-0.1.0.jar

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.2.6.RELEASE)

2015-10-14 09:45:39.241  INFO 23040 --- [           main] hello.Application                        : Starting Application on HL00096.local with PID 23040 (/Users/tannaiyuki/devel/gs-rest-service/initial/build/libs/gs-rest-service-0.1.0.jar started by tannaiyuki in /Users/tannaiyuki/devel/gs-rest-service/initial)
2015-10-14 09:45:39.319  INFO 23040 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@4aec078b: startup date [Wed Oct 14 09:45:39 JST 2015]; root of context hierarchy
2015-10-14 09:45:40.150  INFO 23040 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Overriding bean definition for bean 'beanNameViewResolver': replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter.class]]
2015-10-14 09:45:41.157  INFO 23040 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http)
2015-10-14 09:45:41.558  INFO 23040 --- [           main] o.apache.catalina.core.StandardService   : Starting service Tomcat
2015-10-14 09:45:41.560  INFO 23040 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.0.26
2015-10-14 09:45:41.721  INFO 23040 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2015-10-14 09:45:41.722  INFO 23040 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 2405 ms
2015-10-14 09:45:42.768  INFO 23040 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean        : Mapping servlet: 'dispatcherServlet' to [/]
2015-10-14 09:45:42.774  INFO 23040 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'characterEncodingFilter' to: [/*]
2015-10-14 09:45:42.774  INFO 23040 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2015-10-14 09:45:43.042  INFO 23040 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@4aec078b: startup date [Wed Oct 14 09:45:39 JST 2015]; root of context hierarchy
2015-10-14 09:45:43.129  INFO 23040 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/greeting]}" onto public hello.Greeting hello.GreetingController.greeting(java.lang.String)
2015-10-14 09:45:43.133  INFO 23040 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2015-10-14 09:45:43.134  INFO 23040 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest)
2015-10-14 09:45:43.171  INFO 23040 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2015-10-14 09:45:43.171  INFO 23040 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2015-10-14 09:45:43.227  INFO 23040 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2015-10-14 09:45:43.336  INFO 23040 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2015-10-14 09:45:43.451  INFO 23040 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2015-10-14 09:45:43.453  INFO 23040 --- [           main] hello.Application                        : Started Application in 4.956 seconds (JVM running for 5.682)
2015-10-14 09:45:51.412  INFO 23040 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
2015-10-14 09:45:51.412  INFO 23040 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2015-10-14 09:45:51.431  INFO 23040 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 19 ms

では、ブラウザからアクセスしてみましょう。

execute_1

execute_2

うまく行ったようです。

まとめ

Spring Bootを使って、簡単にアプリケーションを作成することができました。
今回雑に解決したバージョンの部分は、今後適切に対処していきたいです。

参考URL