Vue.jsプロジェクトをjarに内包して実行
フロントエンドはVue.js、バックエンドはJava(SpringBoot)でアプリを作りたいけど、
Vue.jsとJavaでプロジェクトを分けたくない…というシーンに遭遇したので書いておきます
ビルド済みVue.jsリソースをjarファイルに内包してSpringBootの組み込みTomcatで実行します
開発環境
- Windows10Pro
- AdoptOpenJDKJ9 1.8.0_265
- npm 6.14.6
- vue-cli 4.5.4
Vue.js+SpringBootプロジェクトを作成
IntelliJでSpringBootプロジェクトを作成します
以下のコマンドで作成したSpringBootプロジェクトの中にVue.jsプロジェクトを作成します
cd C:\Users\risab\git\demo
vue create web
これでVue.js+SpringBootプロジェクトができました 画像のwebディレクトリがVue.jsプロジェクトです
アプリケーションを実装する
今回はVue.jsで作成した簡単な画面にSpringBootで作成したRESTAPIのレスポンスを表示するアプリを作成します
RESTAPI
/blog/entries/latest
にアクセスすると最新のブログタイトルと記事内容が返される
RESTAPIコントローラーを作成しました
package com.example.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/blog")
public class BlogController {
/**
* 最新ブログ記事を取得
* @return タイトルと内容のMap
*/
@RequestMapping(value = "/entries/latest", method = RequestMethod.GET)
public Entry getLatestEntry() {
Entry entryData = new Entry();
// TODO DBから最新の記事データを取得
entryData.setTitle("VueRouterを使ってみた");
entryData.setContent("VueRouterで簡単にページルーティングができました");
return entryData;
}
/**
* ブログ記事クラス
*/
private class Entry{
private String title;
private String content;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
}
IntelliJでSpringBootアプリを作成するとアプリ起動に必要な以下のクラスは自動で生成されています
DemoApplication.java
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
ServletInitializer.java
package com.example.demo;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(DemoApplication.class);
}
}
アプリ画面
Vue.js+Vuetify+VueRouterで簡単な画面を2つ作成しました
トップページは画面タイトルと「BLOG」ボタンだけのシンプルな画面です
<template>
<v-app>
<v-app-bar
absolute
color="teal lighten-3"
dark
>
<v-toolbar-title>トップページ</v-toolbar-title>
</v-app-bar>
<v-container class="ml-0 mt-16">
<v-row>
<v-col cols="4">
<v-btn
color="brown lighten-3"
width="100"
v-on:click="clickBlogBtn"
>
blog
</v-btn>
</v-col>
</v-row>
</v-container>
</v-app>
</template>
<script>
export default {
name: "TopPage",
methods: {
clickBlogBtn: function () {
this.$router.push("/blogPage")
}
}
}
</script>
<style>
</style>
ブログページは先ほど作成したRESTAPIを呼び出してブログ記事データを表示します
<template>
<v-app>
<v-app-bar
absolute
color="teal lighten-3"
dark
>
<v-toolbar-title>tech blog</v-toolbar-title>
</v-app-bar>
<v-container class="ml-0 mt-16">
<v-row>
<v-col cols="4">
<v-btn
color="blue-grey lighten-4"
width="100"
v-on:click="clickTopPageBtn"
>
top page
</v-btn>
</v-col>
</v-row>
<v-row>
<v-card class="ml-3" color="yellow lighten-5">
<v-card-title>{{ blogPost.title }}</v-card-title>
<v-card-text>
{{ blogPost.content }}
</v-card-text>
</v-card>
</v-row>
</v-container>
</v-app>
</template>
<script>
export default {
name: "BlogPage",
data: () => ({
// APIから取得した記事データをバインド
blogPost: {
title: "",
content: ""
}
}),
mounted() {
// RESTAPI呼び出し
const el = this
this.axios.get("/blog/entries/latest")
.then(response => {
el.blogPost = response.data
})
},
methods: {
clickTopPageBtn: function () {
this.$router.push("/")
}
}
}
</script>
<style>
</style>
以下のファイルも修正・作成しました main.jsは変更不要です
App.vue
<template>
<v-app>
<router-view/>
</v-app>
</template>
<script>
export default {
name: 'App'
};
</script>
router.js
import Vue from "vue"
import Router from "vue-router"
import TopPage from "@/pages/TopPage";
import BlogPage from "@/pages/BlogPage";
Vue.use(Router)
export default new Router({
mode: "history",
routes: [
{
path: "/",
name: "トップページ",
component: TopPage
},
{
path: "/blogPage",
name: "ブログ",
component: BlogPage
}
]
})
これでアプリケーションの作成は完了です
ビルド済みVue.jsの出力先を変更
この時点でwebディレクトリで npm run build
するとweb\dist\配下にビルド済みVue.jsリソースが出力されます
web\package.jsonを修正してJavaから見える場所に出力するようにします
scripts.buildを以下のように修正してSpringBootプロジェクトの静的リソースディレクトリに出力するようにします
{
"name": "demo",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build --dest ../src/main/resources/static/",
"lint": "vue-cli-service lint"
},
上記の通りpackage.jsonを修正してビルドすると以下のようになります
cd C:\Users\risab\git\demo\web
npm run build
jarを生成する
jarファイルはgradleで生成するので、以下の記述をbuild.gradleの末尾に追加します
Main-Classには@SpringBootApplication
を付けているクラスを指定します
jar {
manifest {
attributes 'Main-Class': 'DemoApplication'
}
以下のコマンドを実行するとbuild\libs\配下にjarファイルが生成されます
cd C:\Users\risab\git\demo
gradlew bootJar
あとは生成されたjarファイルを実行するだけです!
cd C:\Users\risab\git\demo\build\libs
java -jar demo-0.0.1-SNAPSHOT
Started DemoApplication in 2.893 seconds (JVM running for 3.378)
のように出力されていればSpringBootアプリケーションが起動できています
SpringBootの組み込みTomcatを利用しているので、ブラウザでhttp://localhost:8080/
にアクセスします