LoginSignup
2
4

More than 3 years have passed since last update.

Docker で Vue.js + Spring REST をやってみる

Last updated at Posted at 2021-03-21

001.png

想定する対象

  • Docker の初学者
  • Vue.js の初学者
  • Spring MVC の初学者

目的

  • Docker について初歩的な動作を実践してみます
  • Vue.js について初歩的な動作を実践してみます
  • Spring MVC について初歩的な動作を実践してみます

前提

  • Docker, Docker Compose が使用可能
$ docker --version
Docker version 20.10.5, build 55c4c88
$ docker-compose --version
docker-compose version 1.28.5, build c4eb3a1f
  • JDK, Maven が使用可能
$ java -version
java version "1.8.0_202"
Java(TM) SE Runtime Environment (build 1.8.0_202-b08)
Java HotSpot(TM) 64-Bit Server VM (build 25.202-b08, mixed mode)
$ mvn -version
Apache Maven 3.6.1 (d66c9c0b3152b2e69ee9bac180bb8fcc8e6af555; 2019-04-05T04:00:29+09:00)

ゴール

  • アプリケーション の仕様
No URL Docker イメージ 概要・用途 実装技術 備考
1 http://localhost:8880/index.html nginx フロントエンド HTML, Javascript(Vue.js), css WEBコンテンツを提供します
2 http://localhost:8881/api/message jetty バックエンド Java(Spring MVC) APIを提供します ※今回は RDBMS 部分は省略

目次

  1. Docker を使ってみる
  2. HTML ファイルを表示させる
  3. 配布可能な Docker アプリを作成する(※簡易版)
  4. Vue.js を導入する
  5. Ajax リクエスト処理の追加
  6. バックエンドサーバーの構築
  7. Java WEBアプリの作成
  8. CSS フレームワークを適用

Docker を使ってみる

  • 既存の Docker イメージを使ってみます

Docker イメージの取得

  • nginx Docker イメージを取得します
$ docker pull nginx:latest
  • 取得済みのイメージを確認します
$ docker images
REPOSITORY   TAG       IMAGE ID       CREATED      SIZE
nginx        latest    6084105296a9   7 days ago   133MB

コンテナの作成と実行

  • コンテナを作成して実行します
$ docker run --name nginx01 -d -p 8880:80 nginx:latest
  • WEBブラウザで確認します
http://localhost:8880/
※ Nginx コンテナが立ち上がっています
  • 同じイメージから別の nginx コンテナを立てることも出来ます ※ポートを別にします
$ docker run --name nginx02 -d -p 7770:80 nginx:latest
  • WEBブラウザで確認します
http://localhost:7770/
※ Nginx コンテナが立ち上がっています

コンテナの停止と削除

  • コンテナの停止
$ docker stop nginx01
$ docker stop nginx02
  • コンテナの再実行
$ docker start nginx01
  • コンテナの状態確認
$ docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED       STATUS         PORTS                  NAMES
29f2e4e799c7   nginx:latest   "/docker-entrypoint.…"   3 hours ago   Up 6 seconds   0.0.0.0:8880->80/tcp   nginx01
  • コンテナの削除
$ docker rm nginx01
$ docker rm nginx02
  • コンテナの状態確認(※停止したコンテナ含む)
$ docker ps -a
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

Docker イメージの削除

  • Docker イメージの確認
$ docker images
REPOSITORY   TAG       IMAGE ID       CREATED      SIZE
nginx        latest    6084105296a9   7 days ago   133MB
  • Docker イメージの削除
$ docker rmi nginx:latest
  • Docker イメージの再確認
$ docker images
REPOSITORY   TAG       IMAGE ID       CREATED      SIZE

<ここまでのまとめ>

  • 既に公開されている nginx の Docker イメージ を使用することが出来ます
  • nginx イメージ から複数の異なる設定の nginx コンテナを立ち上げることが出来ます
  • コンテナを起動、停止、削除することが出来ます
  • nginx イメージを削除することが出来ます
  • ここまでは Docker 環境の中で実行可能です

HTML ファイルを表示させる

  • アプリ開発の為の特定のディレクトリが必要になります、以下を開発ディレクトリとしました
C:\Users\someone\Desktop\app01

※以降、各自の環境で読み替えて下さい

  • 作成するファイルの概要
No ファイルPATH 拡張子 記述言語 概要・内容
1 var\www\html\index.html html HTML WEBコンテンツ
2 docker-compose.yml yml YML Docker 起動用設定

HTML コンテンツの作成

  • HTMLファイルの作成
    ※以下のようにファイルを作成します
※アプリディレクトリ/var/www/html/index.html
※アプリディレクトリで
$ mkdir -p var/www/html
$ touch var/www/html/index.html
  • index.html の内容
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>index</title>
  </head>
  <body>
    <div>It works!</div>
  </body>
</html>

Docker Compose の設定ファイルの作成

  • docker-compose.yml の作成
    ※以下のようにファイルを作成します
※アプリディレクトリで
$ touch docker-compose.yml
  • docker-compose.yml の内容
version: '3'
services:
  nginx:
    image: nginx:latest
    ports:
      - "8880:80"
    volumes:
      - ./var/www/html:/usr/share/nginx/html
    container_name: nginx01
  • Docker Compose を実行します
※アプリディレクトリで
$ docker-compose up -d
Creating nginx01 ... done
  • WEBブラウザで確認します
http://localhost:8880/
※ It works! とWEBブラウザに表示されます
  • <ポイント>
    • docker-compose.ymlvolumes: の項目でローカルPCの HTML コンテンツを nginx コンテナに渡して(マウントして)います
    • 上記の docker-compose.yml は以下の docker run コマンドとほぼ同義です
$ docker run -v "C:\Users\someone\Desktop\app01\var\www\html:/usr/share/nginx/html" --name nginx01 -d -p 8880:80 nginx:latest 

<ここまでのまとめ>

  • Docker 環境があればローカルのHTMLファイルを手軽に表示することが出来ます
  • 既存の nginx イメージを利用する場合 Dockerfile は必要ありません
  • docker-compose.yml は複雑なパラメータを持つ docker run コマンドの代用となります

配布可能な Docker アプリを作成する(※簡易版)

  • app01 フォルダを丸ごと zip 圧縮なりして配布します
  • zip ファイルを解凍し docker-compose.yml が存在するディレクトリで docker-compose up します
  • もちろん相手側の環境に Docker 環境がインストールされている必要があります

<ここまでのまとめ>

  • Docker コンテナとは Docker 環境で実行出来るアプリケーションといえます
  • Docker イメージとは Docker コンテナを作成するテンプレートとして使用されます
  • 静的WEBコンテンツを表示する場合、今回の設定例ですとアプリディレクトリの /var/www/html 配下にWEBコンテンツ(html, js, css, jpg, png, etc)を配置すれば手軽に nginx コンテナを立ち上げて確認することが出来ます

Vue.js を導入する

  • 作成・修正するファイルの概要
No ファイルPATH 拡張子 記述言語 概要・内容
1 var\www\html\index.html html HTML WEBコンテンツ
2 var\www\html\scripts\app.js js javascript WEBコンテンツ・コードビハインド

テキストの表示

  • index.html の修正
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
    <script src="/scripts/app.js" language="javascript" type="text/javascript"></script>
    <title>index</title>
  </head>
  <body>
    <div id="vue-app">
      <div>{{ message }}</div>
    </div>
  </body>
</html>
  • この段階でWEBブラウザで表示してみます
http://localhost:8880/
※ {{ message }} とWEBブラウザに表示されます
※ この状態では vue の処理が走っていません
  • app.js の作成
    ※以下のようにファイルを作成します
※アプリディレクトリ/var/www/html/scripts/app.js
※アプリディレクトリで
$ mkdir -p var/www/html/scripts
$ touch var/www/html/scripts/app.js
  • app.js の内容
// データオブジェクト
var data = { message: "It works!" };

window.addEventListener("load", () => {
    // Vue インスタンスにオブジェクトを追加する
    var vm = new Vue({
        el: '#vue-app',
        data: data
    })
});
  • WEBブラウザで確認します
http://localhost:8880/
※ It works! とWEBブラウザに表示されます
  • <ポイント>
    • app.js で javascript の data オブジェクトを Vue.js のリアクティブシステム data に設定しています
    • その結果 data オブジェクトの message プロパティにを index.html に変数名 message としてバインドすることが出来ます

※参考: Vue のリアクティブシステムとは?

  • index.html をWEBブラウザ(Chrome)などで表示した状態で
    • Developer Tools(F12) を開きます
    • Console のタブに以下のように入力します
> data.messege="テスト"
※ エンターを押します
※ ページの表示にも "テスト" が反映されています
  • <ポイント>
    • javascript の data オブジェクトが Vue のリアクティブシステムに設定されているため自動的にHTML側の表示が更新されます
      • このような動作をリアクティブと呼びます

ボタンの追加

  • index.html の修正
    ※以下のようにファイルを修正します
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
    <script src="/scripts/app.js" language="javascript" type="text/javascript"></script>
    <title>index</title>
  </head>
  <body>
    <div id="vue-app">
      <div>{{ message }}</div>
      <button v-on:click="() => { message = 'Button Clicked!' }">Click</button>
    </div>
  </body>
</html>
  • WEBブラウザで確認します
http://localhost:8880/
※ [Call]ボタンを押すと Button Clicked! とWEBブラウザに表示されます
  • <ポイント>
    • index.html に追加したボタンに Vue の v-on:click ディレクティブを設定しています
    • この例ではボタンをクリックした時に実行される javascript の 関数 を直接記述しています
    • message は Vue に設定した data.message が参照されています
    • Vue のリアクティブシステムにより HTML側の message も自動的に更新されます

イベントメソッドの実装

  • index.html の修正
    ※以下のようにファイルを修正します
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
    <script src="/scripts/app.js" language="javascript" type="text/javascript"></script>
    <title>index</title>
  </head>
  <body>
    <div id="vue-app">
      <div>{{ message }}</div>
      <button v-on:click="getMessage">Click</button>
    </div>
  </body>
</html>
  • WEBブラウザで確認します
http://localhost:8880/
※ 真っ白(エラー)になります 'getMessage' メソッドを記述していないからです
関数を記述してみる(※参考)
  • app.js の修正
    ※以下のように修正してみます
// データオブジェクト
var data = { message: "It works!" };

// 関数(オブジェクト)
var getMessage = () => { message = 'Button Clicked!' };

window.addEventListener("load", () => {
    // Vue インスタンスにオブジェクトを追加する
    var vm = new Vue({
        el: '#vue-app',
        data: data
    })
});
  • WEBブラウザで確認します
http://localhost:8880/
※ [Call]ボタンを押しても何も変化がありません
  • さらに app.js の修正
    ※以下のように修正してみます
// データオブジェクト
var data = { message: "It works!" };

// 関数(オブジェクト)
var getMessage = (event) => { event.view.data.message = 'Button Clicked!' };

window.addEventListener("load", () => {
    // Vue インスタンスにオブジェクトを追加する
    var vm = new Vue({
        el: '#vue-app',
        data: data
    })
});
  • WEBブラウザで確認します
http://localhost:8880/
※ [Call]ボタンを押すと Button Clicked! とWEBブラウザに表示されます
  • <ポイント>
    • Vue の v-on:click ディレクティブから javascript の関数(オブジェクト)を呼び出せます
    • その際、イベント引数が暗黙的に渡って来ます
    • 【注意!】この方法は Vue の一般的な流儀ではありません、通常は次に説明する方法でイベントハンドラを記述します
      • 動作の理解のために説明しました

イベントハンドラを記述する(※実験版)

  • app.js の修正
    ※以下のように修正します
// データオブジェクト
var data = { message: "It works!" }

window.addEventListener("load", () => {
    // Vue インスタンスにオブジェクトを追加する
    var vm = new Vue({
        el: '#vue-app',
        data: data,
        // `methods` オブジェクトの下にメソッドを定義する
        methods: {
            getMessage: () => {
                data.message = 'Button Clicked!';
            }
        }
    })
});
  • WEBブラウザで確認します
http://localhost:8880/
※ [Call]ボタンを押すと Button Clicked! とWEBブラウザに表示されます
  • <ポイント>
    • Vue インスタンスに methods オブジェクトを追加し、getMessage 関数(オブジェクト)を記述します
    • HTMLページ message へのアクセスが data.message になる点に注意してください
    • これは グローバルなオブジェクト var data を参照しています
    • 【注意!】この方法は Vue の一般的な流儀ではありません、通常は次に説明する方法でイベントハンドラを記述します
      • 動作の理解のために説明しました

イベントハンドラを記述する(※Vueの流儀)

  • app.js の修正
    ※以下のように修正します
// データオブジェクト
var data = { message: "It works!" }

window.addEventListener("load", () => {
    // Vue インスタンスにオブジェクトを追加する
    var vm = new Vue({
        el: '#vue-app',
        data: data,
        // `methods` オブジェクトの下にメソッドを定義する
        methods: {
            getMessage() {
                this.message = 'Button Clicked!';
            }
        }
    })
});
  • WEBブラウザで確認します
http://localhost:8880/
※ [Call]ボタンを押すと Button Clicked! とWEBブラウザに表示されます
  • <ポイント>
    • 今回 getMessage メソッドは function を使用しない省略記法で記述しています
    • この記法だとイベントハンドラ内で this を参照可能です
    • ※ Vue の data オブジェクトの message プロパティを this.message で参照出来るのが奇妙に見えますが Vue の流儀なので慣れるしかないと思います

<ここまでのまとめ>

  • このようにシンプルかつ最小限の方法で WEBコンテンツに Vue.js を導入することが出来ます
  • Vue.js を使用することによりWEBコンテンツの ビュー定義実装コード を分離することが可能となります
  • また様々な方法で javascript からオブジェクト変数値を直接操作しても Vue のリアクティブ処理が破綻しないことが分かります

Ajax リクエスト処理の追加

  • HTMLファイルに Ajax リクエスト処理を追加します (axios "アクシオス" 使用)

  • API の仕様

No URL data 要素 データ型 内容
1 http://localhost:8881/api/message massage 文字列 メッセージを返します
2 http://localhost:8881/api/message error bool値 APIのエラー状況を返します
  • 取得する JSON の例
{
    "message": "some message",
    "error": false
}
  • index.html の修正
    ※以下のように修正します
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script src="/scripts/app.js" language="javascript" type="text/javascript"></script>
    <title>index</title>
  </head>
  <body>
    <div id="vue-app">
      <div>{{ message }}</div>
      <button v-on:click="getMessage">Call</button>
    </div>
  </body>
</html>
  • app.jsの修正
    ※以下のように修正します
// データオブジェクト
var data = { message: "It works!" }

window.addEventListener("load", () => {
    // Vue インスタンスにオブジェクトを追加する
    var vm = new Vue({
        el: '#vue-app',
        data: data,
        // `methods` オブジェクトの下にメソッドを定義する
        methods: {
            getMessage() {
                // バックエンドサーバーへ Ajax リクエスト
                axios.get(
                    'http://localhost:8881/api/message', {
                        headers: {'content-type': 'application/json'}
                    })
                    .then(response => {
                        // Ajax リクエストの戻り値を取得
                        this.message = response.data.message;
                    })
                    .catch(error => {
                        // エラーメッセージを表示
                        this.message = error;
                    });
            }
        }
    })
});
  • WEBブラウザで確認します
http://localhost:8880/
※ [Call]ボタンを押すと Error: Network Error とWEBブラウザに表示されます
※ API リクエストを受けるバックエンドサーバーの準備が出来ていないからです
  • <ポイント>
    • getMessage メソッドから Ajax通信で http://localhost:8881/api/message をリクエストしています
    • エラーが発生した場合には message に内容を表示します

<ここまでのまとめ>

  • axios ライブラリを使用してバックエンドサーバーに API リクエストを投げることが出来そうです
  • 今回の例では厳密な REST API ではありません、単に HTTP リクエスト GET で JSON を取得します

バックエンドサーバーの構築

  • 修正するファイルの概要
No ファイルPATH 拡張子 記述言語 概要・内容
1 docker-compose.yml yml YML Docker 起動

Jetty コンテナの追加

  • Jetty は JVM系言語の WEBアプリをデプロイする WEBサーバーです

    • ここでは 8881 ポートで起動させます
  • docker-compose.yml の修正

    ※以下のようにファイルを修正します

version: '3'
services:
  nginx:
    image: nginx:latest
    ports:
      - "8880:80"
    volumes:
      - ./var/www/html:/usr/share/nginx/html
    container_name: nginx01

  jetty:
    image: jetty:9.4.38
    ports:
      - "8881:8080"
    volumes:
      - ./target:/var/lib/jetty/webapps
    container_name: jetty01
  • Docker Compose を実行します
※アプリディレクトリで
$ docker-compose up -d
Creating jetty01 ... done
  • WEBブラウザで確認します
http://localhost:8881/
※ Error 404 - Not Found. とWEBブラウザに表示されますが Jetty コンテナは立ち上がっています

<ここまでのまとめ>

  • JVM系 WEBアプリ実行環境 Jetty も docker-compose.yml の設定だけでコンテナが立ち上がります

Java WEBアプリの作成

  • JVM系のWEBアプリは通常 拡張子 .war 形式の zipファイルとして作成されます
  • Jetty の場合 /var/lib/jetty/webapps ディレクトリに *.war ファイルを配置することで自動的にデプロイされます
  • ローカルPCの *.war WEBアプリを docker-compose.ymljettyvolumes 設定でコンテナに配置します
    • ここでは ./target ディレクトリに .war ファイルをビルド出力すれば、自動的にコンテナにデプロイされます

war アプリの作成

  • 作成するファイルの概要
No ファイルPATH 拡張子 記述言語 概要・内容
1 pom.xml xml XML Maven ビルド設定
2 src\main\webapp\WEB-INF\web.xml xml XML サーブレット定義
3 src\main\webapp\WEB-INF\spring\app-config.xml xml XML Spring bean定義
4 src\main\webapp\WEB-INF\spring\mvc-config.xml xml XML Spring bean定義
5 src\main\java\org\example\controller\ApiController.java java Java コントローラクラス
6 src\main\java\org\example\response\Response.java java Java レスポンスクラス
  • 必要なファイルをまとめて作成します
※アプリディレクトリで
$ mkdir -p src/main/webapp/WEB-INF/spring
$ mkdir -p src/main/java/org/example/controller
$ mkdir -p src/main/java/org/example/response
$ touch pom.xml
$ touch src/main/webapp/WEB-INF/web.xml
$ touch src/main/webapp/WEB-INF/spring/app-config.xml
$ touch src/main/webapp/WEB-INF/spring/mvc-config.xml
$ touch src/main/java/org/example/controller/ApiController.java
$ touch src/main/java/org/example/response/Response.java

xml 設定ファイルの作成

Maven の設定
  • Maven ビルド設定である pom.xml の作成
    ※以下のようにファイルを作成します
※アプリディレクトリ/pom.xml
  • pom.xml の内容
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.example</groupId>
    <artifactId>minimum-api</artifactId>
    <packaging>war</packaging>
    <version>1.0.0</version>
    <name>webapp</name>
    <properties>
        <jackson.version>2.11.0</jackson.version>
        <spring.version>5.2.6.RELEASE</spring.version>
        <lombok.version>1.18.12</lombok.version>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!-- Jackson -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.version}</version>
        </dependency>
        <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <!-- Lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <pluginRepositories>
        <pluginRepository>
            <id>central</id>
            <name>Central Repository</name>
            <url>https://repo.maven.apache.org/maven2</url>
            <layout>default</layout>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
            <releases>
                <updatePolicy>never</updatePolicy>
            </releases>
        </pluginRepository>
    </pluginRepositories>
    <repositories>
        <repository>
            <id>central</id>
            <name>Central Repository</name>
            <url>https://repo.maven.apache.org/maven2</url>
            <layout>default</layout>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
    <build>
        <finalName>api</finalName>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
  • <ポイント>
    • dependencies セクションで Jackson(JSOMマッパー), Spring, Lombok の jarライブラリをダウンロードします
    • pluginRepositories, repositories セクションで Maven Central Repository の設定を追加します(※HTTPS対策)
    • build セクションで .war ファイルへビルドする設定を追加します
      • <finalName>api</finalName> と設定することにより api.war というファイル名で ./target ディレクトリに出力されます
      • $ mvn install コマンドでビルドします (※maven-compiler-plugin が良きに実行してくれます)
      • ./target ディレクトリに api.war が出力されることにより Docker Jetty コンテナ側の /var/lib/jetty/webapps にコピーされ自動的に Docker コンテナ側の war アプリも更新されます
        • ※実稼働を想定する場合にはこの方法が唯一のデプロイ方法ではないです
Servlet の設定
  • Servlet とは Java で HTTPリクエスト・レスポンスを扱う Java EE の標準仕様の一部分です

    • JettyTomcat は Servlet API の標準仕様を実装した Java の WEBサーバーです
      • JettyTomcat は Servlet API の実装なので RDBMS へアクセスする機能などはありません
      • RDBMS へアクセスする場合には JPA という標準仕様があり Hibernate などが JPA を実装します
    • Java EE 標準仕様のリファレンス実装として全部入りの GlassFish というアプリケーションサーバソフトがあります
  • Servlet の設定である web.xml の作成

    ※以下のようにファイルを作成します

※アプリディレクトリ/src/main/webapp/WEB-INF/web.xml
  • web.xml の内容
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app__3__1.xsd" version="3.1">
    <display-name>minimum-api</display-name>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/app-config.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring/mvc-config.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
  • <ポイント>
    • org.springframework.web.servlet.DispatcherServlet が Spring Framework として動作する Servlet になります
      • Spring Framework では基本的に全てのリクエスト・レスポンスが DispatcherServlet で行われます
      • また Spring Framework とは関係ない独自の Servletweb.xml に設定することも可能です
        • 超レガシーな Java WEB システムでは1ページのHTMLに一つの Servlet が設定されたケースもあると思います

Spring の設定

  • Spring の設定である app-config.xml, mvc-config.xml の作成
    ※以下のようにファイルを作成します
※アプリディレクトリ/src/main/webapp/WEB-INF/spring/app-config.xml
※アプリディレクトリ/src/main/webapp/WEB-INF/spring/mvc-config.xml
  • app-config.xml の内容
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="org.example"/>
</beans>
  • mvc-config.xml の内容
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    <context:component-scan base-package="org.example"/>
    <mvc:annotation-driven />
</beans>
  • <ポイント>
    • component-scan base-packageorg.example パッケージのアノテーション設定を読み込んでいます
    • mvc:annotation-driven で Spring MVC におけるアノテーション設定による動作を有効にしています
    • この例では app-config.xml, mvc-config.xml の二つの XML ファイルを設定しています
      • 用途によって分けていますが、必ずしもこの方法でないと設定が読み込めないわけではないです

java コードの作成

  • コントローラクラスとレスポンスクラスを作成します
    ※以下のようにファイルを作成します
※アプリディレクトリ/src/main/java/org/example/controller/ApiController.java
※アプリディレクトリ/src/main/java/org/example/response/Response.java
コントローラー POJO クラスの実装
  • ApiController.java の内容
package org.example.controller;

import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import org.example.response.Response;

/**
 * Ajax通信で HTTP リクエストされる Controller POJO クラス
 */
@RequiredArgsConstructor
@Controller
public class ApiController {

    ///////////////////////////////////////////////////////////////////////////
    // Field

    @NonNull
    private ApplicationContext context; // lombok が自動でコンストラクタをつくりそこからインジェクションされます

    ///////////////////////////////////////////////////////////////////////////
    // public methods

    // Ajax リクエストを受けメッセージを返します
    @CrossOrigin
    @RequestMapping(
        value="/message",
        method=RequestMethod.GET,
        headers="Accept=application/json"
    )
    public @ResponseBody Response getMessage() {
        // 本来ならここでビジネスロジックを処理して

        // レスポンス用のオブジェクトを返す
        return (Response) context.getBean(
            "response", // bean の名前を指定
            "Did you call me?", // メッセージの内容
            false
        );
    }
}
  • <ポイント>
    • @RequiredArgsConstructor アノテーションで lombok が自動で必要なコンストラクタをコンパイル時に自動生成します
    • @Controller アノテーションで Spring MVC のコントローラとして動作します
    • @CrossOrigin アノテーションで他サイトからのアクセスを許可します(※Cross-Origin Resource Sharing)
    • @RequestMapping アノテーションで HTTPリクエストを受けます
    • @ResponseBody アノテーションで HTTPレスポンスを返します
      • この例では Jackson ライブラリで POJO を JSON にマッピングしています
レスポンス POJO クラスの実装
  • Response.java の内容
package org.example.response;

import lombok.Getter;
import lombok.NonNull;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

/**
 * JSON にマッピングされる POJO クラス
 */
@Getter
@AllArgsConstructor
@EqualsAndHashCode
@ToString
@Component(value="response") // bean の名前
@Scope(value="prototype")
public class Response {

    ///////////////////////////////////////////////////////////////////////////
    // Field

    @NonNull
    private String message; // メッセージの内容

    @NonNull
    private boolean isError; // エラーが発生したかどうか(※ダミー)
}
  • <ポイント>
    • @Getter, @AllArgsConstructor アノテーション等は lombok が自動で必要なメソッドをコンパイル時に自動生成します
      • lombok ライブラリを使用することでボイラーコードの記述を回避でき、見通しの良いコードを書くことが可能です
    • @Component アノテーションで bean 名 response として Spring Framework に登録します
    • @Scope アノテーションで prototype と指定することにより DI から new 相当でオブジェクトを取得します
      • ※ Spring Framework のデフォルト動作では DI で取得するオブジェクトはシングルトンです

java WEBアプリのビルド

  • ビルドツールとして Maven (メイブン) を使用します

    • 基本的に pom.xml が存在するディレクトリで mvn install コマンドを走らせるとJavaアプリがビルドされます
  • ビルドコマンド を実行します

※アプリディレクトリで
$ mvn install
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  4.041 s
[INFO] Finished at: 2021-03-21T09:23:45+09:00
[INFO] ------------------------------------------------------------------------
  • WEBブラウザで確認します
http://localhost:8880/index.html
※ [Call]ボタンを押すと Did you call me? とWEBブラウザに表示されます

<ここまでのまとめ>

  • フロント側のWEBコンテンツから、バックエンドにAPIリクエストすることが出来ました
  • またバックエンド側は、フロントのAPIリクエストに応答することが出来ました

CSS フレームワークを適用

  • CSS フレームワークとして UIkit を使用します

  • index.html の修正

    ※以下のようにファイルを修正します

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit@3.6.18/dist/css/uikit.min.css" />
    <script src="https://cdn.jsdelivr.net/npm/uikit@3.6.18/dist/js/uikit.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/uikit@3.6.18/dist/js/uikit-icons.min.js"></script>
    <script src="/scripts/app.js" language="javascript" type="text/javascript"></script>
    <title>index</title>
  </head>
  <body>
    <div id="vue-app">
      <div class="uk-section uk-section-small uk-section-secondary"></div>
        <div class="uk-section uk-section-primary">
          <div class="uk-container uk-container-small">
            <h1 class="uk-heading-large">Message</h1>
            <p>
              <span uk-icon="icon: info"></span>
              <span class="uk-text-lead">{{ message }}</span>
            </p>
            <p>
              <button class="uk-button uk-button-large uk-button-primary" v-on:click="getMessage">Call</button>
            </p>
          </div>
        </div>
      <div class="uk-section uk-section-xlarge uk-section-muted"></div>
    </div>
  </body>
</html>
  • WEBブラウザで確認します
http://localhost:8880/index.html

<ここまでのまとめ>

  • CSS フレームワークの導入で見た目を整えることが出来ます

感想

  • バックエンド側の実装を、フロント側からのAPIリクエストに対するJSON応答に限定することにより、システムが疎結合に保たれると思いました
  • バックエンド側で JSP(HTML) のようなフロント部分を提供しない仕組みは、フロント側がスマホネイティブアプリや Unity ゲームなどの場合にはそれらに対して汎用的に利用可能となると思いました

今後の展開は?

  • フロント側の javascript を TypeScript に置き換えると良いかも知れません
    • webpack の設定が必要になります
  • バックエンド側の処理に RDBMS を使用する設定を追加すると良いと思います
    • Java を使用する理由の一つに RDBMS に対する処理が実用的に書けることがあります
    • JPA 仕様を実装する Hibernate 等のライブラリが必要となりますが pom.xml Jetty コンテナに同梱出来ます
    • この場合に必要となるのは MySQL, PostgreSQL の Docker コンテナとなります

リポジトリ

minimum-docker-vue-spring


ありがとうございました

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4