cURL+tarコマンドでSpring Bootアプリの開発プロジェクトを作る

Spring Bootアプリを新規に作る場合、多くの方は以下のいずれかの方法で開発プロジェクトを作成していると思います。

* [SPRING INITIALIZRのWeb UI](https://start.spring.io/)
* IDE(STSやIntelliJ IDEAなど)に組み込まれているSPRING INITIALIZRのプロジェクト作成ウィザード

Web UIやIDEのウィザードを使うと指定可能な依存アーティファクトの検索を行うことができ、入力フォームに値を指定してポチポチするだけで開発プロジェクトが簡単に作成できちゃいます。これは非常に便利で私も愛用していますが、UI操作自体が面倒だな〜と思う時もあります。

そんな時はCLIですよね〜
Spring Bootは、Spring Boot提供のコマンドを使って開発プロジェクトを作成する方法もサポートしていますが、今回は`curl`と`tar`コマンドを使う方法を紹介します。

# スタンドアロンアプリ用のプロジェクトを作る

以下のようなコマンドを実行すると、スタンドアロンアプリ(=非WEB)向けの開発プロジェクトがつくれます。

```
$ curl -s https://start.spring.io/starter.tgz -d baseDir=demo | tar -xzvf -
x demo/mvnw
x demo/
x demo/.mvn/
x demo/.mvn/wrapper/
x demo/src/
x demo/src/main/
x demo/src/main/java/
x demo/src/main/java/com/
x demo/src/main/java/com/example/
x demo/src/main/resources/
x demo/src/test/
x demo/src/test/java/
x demo/src/test/java/com/
x demo/src/test/java/com/example/
x demo/.gitignore
x demo/.mvn/wrapper/maven-wrapper.jar
x demo/.mvn/wrapper/maven-wrapper.properties
x demo/mvnw.cmd
x demo/pom.xml
x demo/src/main/java/com/example/DemoApplication.java
x demo/src/main/resources/application.properties
x demo/src/test/java/com/example/DemoApplicationTests.java
```

```text:ディレクトリ構成
$ tree demo
demo
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── example
    │   │           └── DemoApplication.java
    │   └── resources
    │       └── application.properties
    └── test
        └── java
            └── com
                └── example
                    └── DemoApplicationTests.java
```

ビルドしてみる。

```
$ cd demo
$ ./mvnw clean package
```

実行してみる。

```
$ java -jar target/demo-0.0.1-SNAPSHOT.jar

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

2017-01-03 15:38:28.749  INFO 31974 --- [           main] com.example.DemoApplication              : Starting DemoApplication v0.0.1-SNAPSHOT on Kazuki-no-MacBook-Pro.local with PID 31974 (/usr/local/apps/demo/target/demo-0.0.1-SNAPSHOT.jar started by shimizukazuki in /usr/local/apps/demo)
2017-01-03 15:38:28.751  INFO 31974 --- [           main] com.example.DemoApplication              : No active profile set, falling back to default profiles: default
2017-01-03 15:38:28.794  INFO 31974 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5b37e0d2: startup date [Tue Jan 03 15:38:28 JST 2017]; root of context hierarchy
2017-01-03 15:38:29.312  INFO 31974 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2017-01-03 15:38:29.323  INFO 31974 --- [           main] com.example.DemoApplication              : Started DemoApplication in 0.843 seconds (JVM running for 1.151)
2017-01-03 15:38:29.324  INFO 31974 --- [       Thread-1] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@5b37e0d2: startup date [Tue Jan 03 15:38:28 JST 2017]; root of context hierarchy
2017-01-03 15:38:29.325  INFO 31974 --- [       Thread-1] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown
```

:clap: :clap: :clap:

# WEBアプリ用のプロジェクトを作る

以下のようなコマンド（dependenciesにwebを指定）を実行すると、WEBアプリ向けの開発プロジェクトがつくれます。


```
$ curl -s https://start.spring.io/starter.tgz -d name=web-demo -d artifactId=web-demo -d dependencies=web -d baseDir=web-demo | tar -xzvf -
x web-demo/mvnw
x web-demo/
x web-demo/.mvn/
x web-demo/.mvn/wrapper/
x web-demo/src/
x web-demo/src/main/
x web-demo/src/main/java/
x web-demo/src/main/java/com/
x web-demo/src/main/java/com/example/
x web-demo/src/main/resources/
x web-demo/src/main/resources/static/
x web-demo/src/main/resources/templates/
x web-demo/src/test/
x web-demo/src/test/java/
x web-demo/src/test/java/com/
x web-demo/src/test/java/com/example/
x web-demo/.gitignore
x web-demo/.mvn/wrapper/maven-wrapper.jar
x web-demo/.mvn/wrapper/maven-wrapper.properties
x web-demo/mvnw.cmd
x web-demo/pom.xml
x web-demo/src/main/java/com/example/WebDemoApplication.java
x web-demo/src/main/resources/application.properties
x web-demo/src/test/java/com/example/WebDemoApplicationTests.java
```

```text:ディレクトリ構成
$ tree web-demo
web-demo
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── example
    │   │           └── WebDemoApplication.java
    │   └── resources
    │       ├── application.properties
    │       ├── static
    │       └── templates
    └── test
        └── java
            └── com
                └── example
                    └── WebDemoApplicationTests.java
```

ビルドしてみる。

```
$ cd web-demo
$ ./mvnw clean package
```

実行してみる。

```
$ java -jar target/web-demo-0.0.1-SNAPSHOT.jar

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

2017-01-03 15:48:49.606  INFO 32190 --- [           main] com.example.WebDemoApplication           : Starting WebDemoApplication v0.0.1-SNAPSHOT on Kazuki-no-MacBook-Pro.local with PID 32190 (/usr/local/apps/web-demo/target/web-demo-0.0.1-SNAPSHOT.jar started by shimizukazuki in /usr/local/apps/web-demo)
2017-01-03 15:48:49.608  INFO 32190 --- [           main] com.example.WebDemoApplication           : No active profile set, falling back to default profiles: default
2017-01-03 15:48:49.658  INFO 32190 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@4bf558aa: startup date [Tue Jan 03 15:48:49 JST 2017]; root of context hierarchy
2017-01-03 15:48:50.658  INFO 32190 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http)
2017-01-03 15:48:50.670  INFO 32190 --- [           main] o.apache.catalina.core.StandardService   : Starting service Tomcat
2017-01-03 15:48:50.671  INFO 32190 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.6
2017-01-03 15:48:50.744  INFO 32190 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2017-01-03 15:48:50.744  INFO 32190 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1089 ms
2017-01-03 15:48:50.855  INFO 32190 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: 'dispatcherServlet' to [/]
2017-01-03 15:48:50.858  INFO 32190 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
2017-01-03 15:48:50.858  INFO 32190 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2017-01-03 15:48:50.858  INFO 32190 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2017-01-03 15:48:50.858  INFO 32190 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]
2017-01-03 15:48:51.071  INFO 32190 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@4bf558aa: startup date [Tue Jan 03 15:48:49 JST 2017]; root of context hierarchy
2017-01-03 15:48:51.123  INFO 32190 --- [           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)
2017-01-03 15:48:51.124  INFO 32190 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2017-01-03 15:48:51.147  INFO 32190 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-01-03 15:48:51.147  INFO 32190 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-01-03 15:48:51.173  INFO 32190 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2017-01-03 15:48:51.282  INFO 32190 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2017-01-03 15:48:51.335  INFO 32190 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2017-01-03 15:48:51.338  INFO 32190 --- [           main] com.example.WebDemoApplication           : Started WebDemoApplication in 2.123 seconds (JVM running for 2.45)
```

起動したTomcatにアクセスしてみる。

```
$ curl http://localhost:8080/
{"timestamp":1483426201099,"status":404,"error":"Not Found","message":"No message available","path":"/"}
```

エンドポイントを作っていないので、404 Not Foundエラーになるのは正しい動作ですね。

:clap: :clap: :clap:


# 詳しくは・・・

Spring InitializrのGitHubのREADMEを読みましょう。

* https://github.com/spring-io/initializr#generating-a-project

# まとめ

作るものが予め決まっている場合（例えば、開発環境をセットアップする際の手順などの場合）は・・・GUIを使うのではなく、CLIを使ってワンライナーで（＝コマンドをコピって）開発プロジェクト作る手順にしておくと効率的ですよね。