見出し画像

小さな渋谷の会社でゼロから作るWebアプリケーション (3) JavaそしてSpring Boot

株式会社エー・アンド・ディのCTOの山口です。

前回でGradleプロジェクトの初期化を行いました。

今回は、いよいよ最初のSpring Bootアプリケーションが起動できるところまで行こうと思います。

なお、本マガジンで開発しているプロジェクトのソースコードはこちらで公開しています。
(開発は継続しているため、masterブランチでは本文の内容より先に進んでいる場合があります)

https://github.com/andcorp/morisoba

JAVA_HOMEの設定(続)

前回ではJAVA_HOMEにとりあえず実際のJDKへのフルパスを記載してしまいました。

チーム開発では開発者それぞれでJDKの配置パスが異なることが考えられます。
(パスに含まれるユーザー名などがそれぞれ異なるため)

今回から本格的にVSCodeでJavaを利用していくため、.code-workspaceファイルを自動生成することでJAVA_HOMEも自動設定されるようにします。

具体的には、Gradleの設定ファイル(build.gradle)にワークスペース初期化の処理を書き、.code-workspaceを生成します。

Gradle起動バッチ

まず、Gradleを起動するためのバッチファイルを作成します。

Gradle起動のためにそもそもJAVA_HOME設定などが必要になるため、バッチファイルでそれを簡略化します。
Githubリポジトリーではinitialize.batとして格納されています。

rem バッチファイルの置いてあるディレクトリをカレントディレクトリにする。
cd %~dp0

rem ダウンロードしたJDKのパスをJAVA_HOMEに設定する。
rem フルパス展開のため、CALLを使用する。
CALL :SET_JAVA_HOME "..\jdk\amazon-corretto-11"

rem JAVA_HOMEを設定したうえでGradle起動。
rem --stacktraceでエラー時のスタックトレースを表示する。
rem initializeWorkspace タスクを起動する。
.\gradlew.bat --stacktrace initializeWorkspace

:SET_JAVA_HOME
 rem パラメーターで指定されたJDKへのパスをフルパスにし、JAVA_HOMEに設定する。
 SET JAVA_HOME=%~dpfn1
 EXIT /B

Gradle起動するだけでも結構大変ですね……。
ふつうにJDKやGradleをインストールして使うだけならこんな苦労は要らないのですが、本マガジンではJDKもGradleもプロジェクト固有のものを使う想定なので……。

Gradleタスク作成

次に、masterディレクトリにあるbuild.gradleにGradleタスクを追加します。

Gradleで実行されるさまざまな処理は、以下のようなタスクの形で定義されます。

// ワークスペースの初期化
task initializeWorkspace {
   doFirst { println("こんにちは世界!") }
}

doFirstは、タスクの実行前に行われる処理を記述する記法です。
今回initializeWorkspaceは空のタスクとして作っているので、実際はdoFirstの中の処理だけが行われて、後は何もせずに終了になります。

doFirstの中のprintlnは、JavaのSystem.out.printlnみたいなものです。これでコンソールにメッセージが表示されます。

さて、これでコマンドプロンプトからinitialize.batを実行すると、以下のような表示がされるはずです。

(前略)
C:\Users\ikemen\morisoba\master>.\gradlew.bat --stacktrace initializeWorkspace
To honour the JVM settings for this build a new JVM will be forked. Please consider using the daemon: https://docs.gradle.org/5.5.1/userguide/gradle_daemon.html.
Daemon will be stopped at the end of the build stopping after processing
Configuration on demand is an incubating feature.

> Task :initializeWorkspace
こんにちは世界!

BUILD SUCCESSFUL in 4s
1 actionable task: 1 executed

ちゃんとメッセージが出ました。
タスクの実行が確認できたので、次に、ファイル生成の処理を実装していきます。

Gradleにはファイルコピーの機能が備わっており、そちらを利用します。今回は早くJavaを使うところまで行きたいので、詳しい説明は割愛します。
(詳しくはこちら)

// ファイルパスをエスケープするため、Groovyのライブラリを使用する。
import groovy.json.StringEscapeUtils

// ワークスペースの初期化
task initializeWorkspace {
   doFirst {
       // ファイルコピー実行
       copy {
           // このディレクトリからの相対パスで、テンプレートになるファイルを指定する。
           from file("$rootDir/../morisoba.code-workspace.template")

           // テンプレートファイル内の変数展開を行う。
           // 以下の指定で、 ${userJavaHome} が環境変数のJAVA_HOMEの内容に置換される。
           // ファイルパスにはバックスラッシュ(円記号)が混じるので、JSON向けエスケープ処理を行う。
           expand userJavaHome: StringEscapeUtils.escapeJavaScript(System.getenv()['JAVA_HOME'])

           // コピー後のファイル名を指定する。
           rename { 'morisoba.code-workspace' }

           // コピー先ディレクトリを指定する。
           into "$rootDir/../"
       }
   }
}

morisoba.code-workspace.template は以下のような内容になります。
Githubリポジトリーではこちらに格納されています。

{
    "folders": [
        {
	    "path": "."
        }
    ],
    "extensions": {
        "recommendations": [
	    "EditorConfig.EditorConfig",
            "eamodio.gitlens",
            "redhat.java",
            "vscjava.vscode-java-debug",
            "vscjava.vscode-java-pack",
            "vscjava.vscode-java-test",
            "vscjava.vscode-maven"
        ]
    },
    "settings": {
        "java.home": "${userJavaHome}",
        "java.jdt.ls.vmargs": "-noverify -Dfile.encoding=utf8 -Xmx1G -XX:+UseG1GC -XX:+UseStringDeduplication",
        "java.configuration.updateBuildConfiguration": "automatic",
        "files.exclude": {
            "**/.classpath": true,
            "**/.project": true,
            "**/.settings": true,
            "**/.factorypath": true,
            "jdk/": true
        },
        "terminal.integrated.env.windows": {
            "JAVA_HOME": "${userJavaHome}"
        }
    }
}

上記で ${userJavaHome} となっている箇所が、initialize.batで指定されているの環境変数JAVA_HOMEの内容(つまりダウンロードしたJDKのパス)で置換されます。

ここまでやれば、initialize.batでmorisoba.code-workspaceが生成できるようになります。
あとは、この手順をREADME.mdなどに記載して、JDKダウンロード後・VSCode起動よりも前に行うようにすれば、JAVA_HOMEがきちんと設定された環境が手に入ります。

(なお、実際のbuild.gradleでは、今後の初期化処理の追加も見込んで、.code-workspace生成を別タスクにしてあります)

Spring Bootとは

色々大変でしたが、これで各個人それぞれに対してJAVA_HOMEが正しく設定されているワークスペースができました。
ようやくSpring Bootでのアプリケーションの作成に入れます。

その前に、Spring Bootの説明をしておきます。

が、そのさらに前に、Spring Frameworkの説明をしないとSpring Bootの説明ができません。

Spring Frameworkとは、Javaエンタープライスアプリケーションの包括的なフレームワークです。

どのくらい包括的かというと、以下のような機能がほぼ揃っています。
(これでもごく一部です)
(どんな機能か、理解できなくても今は支障ありません)

* コア機能
        * 依存性注入
        * イベント配信
        * リソース管理
        * 国際化(i18n)
        * バリデーション
        * データバインディング
        * 型変換
        * SpEL
        * AOP
* テスト
        * モックオブジェクト
        * テスト用コンテキスト
        * Spring MVCフレームワーク用テスト
        * テスト用Webクライアント
* データアクセス
        * トランザクション
        * DAO
        * JDBC
        * ORM
        * XMLマーシャリング
* Spring MVC および Spring WebFlux
        * Web用フレームワーク
* 外部システム連携(Integration)
        * リモート制御
        * JMS
        * JCA
        * JMX
        * メール
        * タスク処理
        * スケジューリング
        * キャッシュ
* プログラミング言語サポート
        * Kotlin
        * Groovy
        * その他動的言語
        * (もちろんJavaも)

すごいですね。これはやばいですね。

こんなすごいSpring Frameworkは、最初のリリースが2003年6月で、既に16年を超える歴史があります。
そのため、高機能で実績があり、ユーザー数が多く、安定しています。

が、その代わり、積み重なったものがいろいろあって、そのままだと扱うのが大変です。
そのまま使うと、以下のような地獄が繰り広げられる場合があります。

設定ファイル地獄
XMLなどでいろいろな設定を記載する必要が出てしまい、管理がとても煩雑になる。

依存ライブラリ地獄
使用するライブラリや、それに合わせた設定ファイルがたくさん必要になり、上記の設定ファイル地獄に拍車を掛ける。

このような地獄を回避するのがSpring Bootです。

Spring Bootを使うと、Springの壮大な機能はそのままに、以下のようなメリットが享受できます。

* 単独で起動できるアプリケーションが簡単に作成できる。
* 依存ライブラリに応じて必要な設定が自動的に行われる。
        * 設定は必要最低限でOK
* XMLの設定ファイルなどは不要。

機能はそのままで簡単に使える……というわけで、さっそく使ってみましょう!

Gradle向けの設定ファイル作成

簡単に使えるとはいえ、やっぱりそれなりに準備が必要です。

まず、Java向けのJDKバージョンやソースコードのエンコーディングの設定などをmaster/gradle.propertiesに記載しておきます。

org.gradle.parallel=true
org.gradle.configureondemand=true
org.gradle.workers.max=4
org.gradle.daemon=false

javaVersion=11
productGroup=andcorp.co.jp
productVersion=0.0.1
productName=morisoba
sourceEncoding=UTF-8

javaVersionより下がプロジェクト固有の部分になります。
この設定を後続の作業で使用していきます。

Spring Bootを使う

Spring Bootは、Gradleと組み合わせて使うためのGradle Pluginが提供されています。
これを使うと、起動やビルドでいろいろ便利です。

これを使ったSpring Bootのプロジェクトを作成していきます。

まず、Javaアプリケーションのためのサブプロジェクト backend を作成します。
(フロントエンドも含めたWebアプリケーション全体から見て、Javaはバックエンドサーバーとなるため、backendと命名しました)

サブプロジェクトは、masterディレクトリ内に以下のようなsettings.gradleファイルを配置することで行えます。

includeFlat 'backend'

これで、masterディレクトリと同階層にbackendディレクトリを作成すると、そこをサブプロジェクトにできます。

backendディレクトリができたら、そこでGradleプロジェクトの設定ファイルとなるbuild.gradleを作成します。
Githubリポジトリでは、こちらのファイルが該当します。

// backend プロジェクト ビルド設定

// 使用するプラグイン
plugins {
   id 'org.springframework.boot' version "2.1.7.RELEASE"
}

// デフォルトで入っているJava用プラグインを有効化
apply plugin: 'java'
apply plugin: 'eclipse'

// Spring Boot 依存関係管理用プラグイン有効化
apply plugin: 'io.spring.dependency-management'

// リポジトリ設定
repositories {
   jcenter()
}

// Java設定。先ほどのgradle.propertiesの値を使用する。
sourceCompatibility = javaVersion
targetCompatibility = javaVersion
version = productVersion
compileJava.options.encoding = sourceEncoding
compileTestJava.options.encoding = sourceEncoding

// 依存関係の設定
dependencies {
   implementation 'org.springframework.boot:spring-boot-starter-web'
}

// 依存関係の更新
task updateDependencies(dependsOn: ['eclipse', 'classes', 'testClasses']) {
}

// プロジェクトの初期化
task initializeProjects(dependsOn: 'updateDependencies') {
}

なんか一気にいろいろ書いてしまいましたが、今のところはこんなものだと思ってください。

後で細かく手を入れていくうちに理解できると思います。

上記の設定で、initializeProjectsというタスクを実行することで、必要な依存ライブラリがダウンロードされるようになっています。
以下のコマンドで、backendプロジェクトのタスクinitializeProjectsを実行できます。

C:\Users\ikemen\morisoba\master>.\gradlew.bat :backend:initializeProjects

Application.javaの作成

ついにようやくJavaのソースコードを書くところまでたどり着きました……。

GradleのJavaプロジェクトでは、下記のディレクトリにソースコードとリソースファイルを配置します。

* src/main/java
* src/main/resources
* src/test/java
* src/test/resources

今回はとりあえず、src/main/javaにアプリケーションのmainメソッドを実装するApplication.javaを作成します。

パッケージ構造は、Javaの流儀に従って下記の通りにしました。

* src/main/java/
        * jp/co/andcorp/morisoba/
                * Application.java

そしてこちらが中身です。最小のSpring Bootアプリケーションです!

package jp.co.andcorp.morisoba;

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

/**
* アプリケーションクラス.
*/
@SpringBootApplication
public class Application {

   /**
    * アプリケーションのメインメソッド.
    *
    * @param args コマンドライン引数
    */
   public static void main(final String[] args) {
       SpringApplication.run(Application.class, args);
   }
}

これだけあれば、Gradleでこのアプリケーションを起動することができます!

C:\Users\ikemen\morisoba\master>.\gradlew.bat :backend:bootRun
To honour the JVM settings for this build a new JVM will be forked. Please consider using the daemon: https://docs.gradle.org/5.5.1/userguide/gradle_daemon.html.
Daemon will be stopped at the end of the build stopping after processing
Configuration on demand is an incubating feature.

> Task :backend:bootRun

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

2019-08-27 21:28:04.946  INFO 13580 --- [           main] jp.co.andcorp.morisoba.Application       : Starting Application on IKEMEN-PC with PID 12345 (C:\Users\ikemen\morisoba\backend\build\classes\java\main started by ikemen in C:\Users\ikemen\morisoba\backend)
2019-08-27 21:28:04.947  INFO 13580 --- [           main] jp.co.andcorp.morisoba.Application       : No active profile set, falling back to default profiles: default
2019-08-27 21:28:05.975  INFO 13580 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2019-08-27 21:28:06.002  INFO 13580 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2019-08-27 21:28:06.002  INFO 13580 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.22]
2019-08-27 21:28:06.124  INFO 13580 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2019-08-27 21:28:06.125  INFO 13580 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1147 ms
2019-08-27 21:28:06.300  INFO 13580 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2019-08-27 21:28:06.452  INFO 13580 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2019-08-27 21:28:06.456  INFO 13580 --- [           main] jp.co.andcorp.morisoba.Application       : Started Application in 1.829 seconds (JVM running for 2.92)
<=========----> 75% EXECUTING [27s]
> :backend:bootRun

この状態で、 http://localhost:8080 にアクセスすると、一応画面が出ます。

画像1

はい404エラーですね。

でも、まだ何も作っていないので当然です。このエラーが出ていること自体が、ちゃんとSpring Bootが起動している証拠です!

ここまで来るのが長かった……。

ちなみに、起動したアプリケーションを落とすには、キーボードでCtrl + Cを何度か押す必要があります。

2019-08-27 21:28:04.946  INFO 13580 --- [           main] jp.co.andcorp.morisoba.Application       : Starting Application on IKEMEN-PC with PID 12345 (C:\Users\ikemen\morisoba\backend\build\classes\java\main started by ikemen in C:\Users\ikemen\morisoba\backend)
2019-08-27 21:28:04.947  INFO 13580 --- [           main] jp.co.andcorp.morisoba.Application       : No active profile set, falling back to default profiles: default
2019-08-27 21:28:05.975  INFO 13580 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2019-08-27 21:28:06.002  INFO 13580 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2019-08-27 21:28:06.002  INFO 13580 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.22]
2019-08-27 21:28:06.124  INFO 13580 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2019-08-27 21:28:06.125  INFO 13580 --- [           main] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1147 ms
2019-08-27 21:28:06.300  INFO 13580 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2019-08-27 21:28:06.452  INFO 13580 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2019-08-27 21:28:06.456  INFO 13580 --- [           main] jp.co.andcorp.morisoba.Application       : Started Application in 1.829 seconds (JVM running for 2.92)
<=========----> 75% EXECUTING [27s]
> :backend:bootRun
バッチ ジョブを終了しますか (Y/N)? Y

「バッチ ジョブを終了しますか (Y/N)?」が出たところでY + Enterを押せば終了します。

GradleタスクをVSCodeから実行する

VSCodeのタスク機能を使用することで、Gradleのタスクをコマンドプロンプトやターミナルから叩かなくても済むようになります。
この機会に設定を済ませておきます。

VSCodeのメニューの「ターミナル」>「タスクの構成」を選択します。

画像2

画像3

上記の「テンプレートからtasks.jsonを生成」を選択します。
すると、.vscodeディレクトリが作成され、中にtasks.jsonができます。

tasks.jsonを下記のようにすると、先ほどのinitializeProjectsが「タスクの実行」から実行できるようになります。

{
   // See https://go.microsoft.com/fwlink/?LinkId=733558
   // for the documentation about the tasks.json format
   "version": "2.0.0",
   "tasks": [
       {
           "label": "initializeProjects",
           "type": "shell",
           "command": ".\\gradlew.bat initializeProjects",
           "options": {
               "cwd": ".\\master"
           },
           "problemMatcher": []
       },
   ]
}

画像4

bootRunも追加しておきましょう。
実質バックエンドサーバーの起動のため、タスク名はserveBackendに変更しておきます。

{
   // See https://go.microsoft.com/fwlink/?LinkId=733558
   // for the documentation about the tasks.json format
   "version": "2.0.0",
   "tasks": [
       {
           "label": "initializeProjects",
           "type": "shell",
           "command": ".\\gradlew.bat initializeProjects",
           "options": {
               "cwd": ".\\master"
           },
           "problemMatcher": []
       },
       {
           "label": "serveBackend",
           "type": "shell",
           "command": ".\\gradlew.bat :backend:bootRun",
           "options": {
               "cwd": ".\\master"
           },
           "problemMatcher": []
       }
   ]
}

画像5

まとめ

ようやくJavaのWebアプリケーション(まだ何も中身が無いですが……)を起動できるところまで来ました。

次回は、Javaのコーディングを支援する各種機能を有効にしていきたいと思います。

免責事項・ライセンス

画像6

 この 作品 は クリエイティブ・コモンズ 表示 - 改変禁止 4.0 国際 ライセンスの下に提供されています。

 弊社の当コンテンツに掲載されている情報の正確性・安全性などについての保証はなく、弊社は何らの責任を負うものではありません。
 弊社の当コンテンツに掲載された内容によって生じた損害等の一切の責任を負いかねますので、ご了承ください。

Copyright © 2019 ANDCORP.

この記事が気に入ったらサポートをしてみませんか?