Closed23

Java(SpringBoot)+ Eclipceのはじめかた

104104
  • JavaはJava言語とJVMの総称である。
  • Javaのプログラムを開発するにはJDKが必要になるが、この開発を担っているのはOpenJDKプロジェクトであるとのこと。
  • このOpenJDKをもとにいろいろなベンダーが実行可能なパッケージとしてディストリビューションを配布している。

https://gihyo.jp/article/2022/07/java2022-0701

104104

何をインストールすれば良いのか

  • JDK(Java Development Kit)をダウンロードしてインストールする。
    • Javaの仕様は複数存在するがSE(Standard Edition)が名前の通り最も標準
    • ディストリビューションも多数存在するが、何も考えずに「JDK インストール」と検索して出てくるのはOracle製のJDK

JDKのバージョン

  • ディストリビューションは多数存在するが、バージョンに用いられる数字は標準化されている模様。
  • Javaの17 と JDKの17は同じ言語仕様のJavaを扱うと考えて問題ない。
  • 現行でどのバージョンを選べば良いかについては、多くで長期サポートのバージョン(LTS)が推薦されており、以下の記事を見ると現在は17か21にしておくのが良さそう。
    • 8→11のバージョンアップはきついと多くで言われているのを見かける。

https://irof.hateblo.jp/entry/2023/06/07/023118

104104

SpringBootでWebアプリを作成する

これまで他言語を学習してきた中で感じているのは、
入門書を愚直に1からやっていくのは退屈で、
Webアプリケーション等作りながら手を動かしてやっていく方が性に合っている気がする。

104104

IDEの選定

JavaでメジャーなフレームワークはSpirng Framwork、SpringBootあたりであるが、Springアプリケーションの開発コミュニティからSTS(Spring Tool Suite)というIDEがある。これはEclipceベースにSpring開発に必要なものを足したものなので、Eclipceは内含されている。

もう1つ、JavaのIDEでメジャーなものにEclipceが存在するが、これもEclipceのマーケットプレイスからSTSのプラグインを導入しているのであればSTSに近い環境を用意することができる模様。

eclipseとSTSって何が違うのですか?

SpringとSpring Framework 、SpringBootの関係

Javaの文脈でSpringといったときはSpring Frameworkを指すっぽい。

Spring Frameworkという名前を聞いた時に「何かのフレームワークなんだな」と思うが、これが関係の理解をややこしくしていると思う。
Spring Frameworkはアプリケーション開発に必要なフレームワーク(モジュール)の集合体であり、一部具体例としてはSpring Security、Spring MVCというモジュールを内含している。

SpringBootは設定が複雑なSpringFramworkの簡易版として紹介されることが多いが、SpringFramworkのSpringMVCがベースとなっている。
BootもSpringFramworkというフレームワークの集合体の一部として位置付けられており、派生して独立したという扱いでもないらしい。

Spring bootとは?Spring frameworkとの違いは?

STSバージョン3までは「ファイル」->「新規」に「Spring Legacy Project」というメニューがあり、そこからSpringFramwork(SpringMVC)を扱うことができたらしいが、STS4からはこのメニューは廃止されてしまった模様。

Spring tool suiteのSpring legacy project選択時、ファイルが自動生成されない

ただ、またややこしいのがSpringFramwork自体の開発が終了した訳ではなく、2022年にバージョン6がリリースされている。

104104

Tomcatとは

正式にはApache Tomcat。Wikipediaの引用は以下の通り。

Apache Tomcat(アパッチ トムキャット)は、Java ServletやJavaServer Pages (JSP) を実行するためのWebコンテナ(サーブレットコンテナ、サーブレットエンジン)である。Apache License 2.0を採用したオープンソースソフトウェア。

Apache HTTP Serverとの違い

Tomcatという名称だけは聞いたことあるが、Apacheが開発していたとは知らなかった。

PHPerとしてはApacheと聞くとApache HTTP Serverの方をイメージする。

いまさら聞けない「Apache HTTP Server」と「Apache Tomcat」の違いとは?

Apache HTTP ServerはWebサーバーであり、ApacheTomcatはアプリケーションサーバーであると、そもそもの性質が異なる。

ウェブサーバーとアプリケーションサーバーの違い - テクノロジーサーバーの違い - AWS

超平たく言うとWebサーバーはHTTPやCSS、JavaScriptなどの静的コンテンツを配信、アプリケーションサーバーは動的なコンテンツを配信する。
HTTP ServerがPHPなどの動的なコンテンツを配信できるのはモジュールによってアプリケーションサーバーに相当する機能が拡張されているからである。

また、Wikiでの説明にはコンテナという単語が出てきたが、Dockerのコンテナとは関係ない。

Apache Tomcatとは何なのか?

TomcatはJava ServletやJSP(JavaServer Pages)を実行するためのサーブレットコンテナであるとWikiでは説明されていた。

Java ServletやJSPとはサーバー上で実際に動かすプログラムのことで、Zip形式のWARファイル化したものををTomcatにデプロイすることでアプリケーションとして実行することができる。

さらに上記のTomcatやアプリケーションサーバーはJVMと呼ばれるJava仮想マシン上で動いている。

Difference between the Apache HTTP Server and Apache Tomcat? - Stack Overflow

104104

今回はWebAPIでURL叩くとHello World返すところまで作成して、AWSなりGCPにデプロイするところまでやってみたい。

EclipceでSpringBootの新規プロジェクト作成

Eclipceのメニューバーから「ファイル」->「新規」-> 「Spring スターター・プロジェクト」を選択する。

JarとWarの選択については、WebAPI想定なのでWarを選択することにした。
それ以外はデフォルトの設定を適用(Javaのバージョンは21、ビルドツールはGradle)し、オプションにはMySQLドライバーを追加した。

JarとWarの違い

Jar(Java Archive)はスタンドアロンなアプリケーション(ローカルマシンで実行するようなアプリケーション)の作成を想定、War(Web Archive)はWebアプリケーションの作成を想定しているとのこと。

分かりやすいところで言えばJarにはサーバー(組み込みのTomcat)が含まれており、Warの場合は本番環境等で別のサーバーが必要になる。

SpringBootの初期ディレクトリ

├─ gradle
│  └─ wrapper
│     ├─ gradle-wrapper.jar
│     └─ gradle-wrapper.properties
├─ src
│  ├─ main
│  │  ├─ java
│  │  │  └─ [your base package]
│  │  └─ resources
│  │     ├─ static
│  │     ├─ templates
│  │     └─ application.properties
│  └─ test
│     ├─ java
│     │  └─ [your base package]
│     └─ resources
├─ .gitignore
├─ build.gradle
├─ gradlew
├─ gradlew.bat
└─ settings.gradle

(上記のディレクトリ図はchatGPTに出してもらった)。

Githubの公開リポジトリでSpringBootのプロジェクトで一番上のmacrozheng/mallを見ると、mainディレクトリにcontrollerやservice、configなど役割ごとにディレクトリを切ってソースコードが格納されている。

パッケージの作成

先ほどのcontrollerやservice、configなどのディレクトリ「パッケージ」という概念で区切られている。

パッケージと聞くと「ある特定の機能を提供するためのファイル群」、つまりライブラリやモジュールが想起される(実際にSwiftとかのパッケージの定義はそう)が、Javaでいうパッケージはクラス名衝突を避けるための名前空間を提供する機能である。

プロジェクト作成時のセットアップ画面にもパッケージを指定する欄があり、デフォルトの「com.examle.demo」のまま作成すると「src/main/java/com/example/demo」と「.」で区切ったディレクトリ階層が生成されている。

macrozheng/mallではdemoディレクトリ以下にパッケージが並んでいるので、今回はこれに倣ってパッケージを作成したい。

こちらに

SpringBootでHelloWorldを動かす環境を用意する

Dockerを使ってSpringBootの開発環境を用意していきたい。
アプリケーションのコンテナ(SpringBootを動かすコンテナ)については、Dockerfileに書くべき内容が分かっていないので、最初はMySQLコンテナだけ立てて後から拡張していく。

104104

Hello Worldを返すRestAPIを作成する

ルール

  • ファイル名とクラス名は同一にする必要がある
  • packageキーワードを使って名前空間を明示する必要がある
  • RestControllerをインポート
104104

パッケージを作成

ファイルメニューからパッケージの作成。

コントローラクラスを作成

package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @GetMapping("/hello")
    public String hello() {
        return "Hello World";
    }
}

HelloController.javaファイルを作成し、上記のように記述。

https://spring.pleiades.io/spring-boot/docs/current/reference/html/getting-started.html#getting-started.first-application.code.mvc-annotations

104104

githubにコミットしてみる

準備

EclipceにEGit(統合Git)がインストールされているか確認

ローカルリポジトリの初期化

  1. プロジェクトエクスプローラからローカルリポジトリを初期化したいプロジェクトを右クリックで選択し、「チーム」->「プロジェクトの共用」と選択

  2. 「プロジェクトの親フォルダー内のリポジトリーを使用または作成」にチェックを入れる。

ここで「Eclipse ワークスペースにリポジトリーを作成することはお勧めしません」と警告が出て先に進めず「!?!?!?」ってなったが、画面下半分にある「リポジトリの作成」というボタンを押せばプロジェクトルートに.gitディレクトリが作成される。

  1. 「完了」を押す。

ファーストコミットを行う

  1. 再びプロジェクトを右クリックして、「チーム」内のオプションを見ると先ほどと表示項目が変わっている。「コミット」を選択。

  2. リストよりも「プレゼンテーション」->「ツリー表示」に切り替えた方が見やすいと思う。

  3. コミットしたい変更をステージし、メッセージを入れてコミットを実行する。

リモートリポジトリにpush

  1. リモートリポジトリ(今回はgithub上に)を作成

  2. eclipce上で「チーム」->「ブランチのpush'main'」を選択
    ※デフォルトのmasterブランチからmainブランチに変更しています。

  3. 以下のようにssh接続先を記載
    ユーザー名とパスワードは不要で、使用しているPCで定義されたsshの接続先が使用される。
    ※自分は複数のリモートリポジトリの設定を同居させていた時期があり、/.ssh/configにホスト名104devとしてgithub.comとは別の設定構成を立てています。

  4. 以下のように「新規ブランチ」と表示されていることを確認し、プッシュを実行。

104104

EclipceのエクスプローラとVSCodeとの使い勝手の違い

  • EclipceにはVSCodeのようなファイルエクスプローラは存在しない。
  • ワークスペースを指定して、その直下に任意のプロジェクトを配置して運用する前提っぽい。
  • 上メニューから「ファイル」->「ワークスペースの切り替え」でSpringBootのプロジェクトのルートを指定したが、プロジェクト単位での認識がされていないためか、プロジェクトエクスプローラには以下のように何も表示されなかった。
104104

自動補完の設定

  • VSCodeの場合は言語専用の拡張機能をインストールすることによって補完機能が強化されるが、Eclipceの場合は設定から補完を効かせる先頭の文字を定義しなくてはならない。
  • 「設定」->「Java」->「エディター」-> 「コンテンツアシスト」を開いて、Javaの有効化トリガーに全てのアルファベットとアンダースコアを定義する。
104104

DB接続設定

DockerでDBのコンテナを立てる

docker-compose.yml
version: "3"
services:
  mysql:
    platform: linux/x86_64
    image: mysql:8
    container_name: mysql
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: app_db
      MYSQL_USER: app_db
      MYSQL_PASSWORD: secret
      TZ: "Asia/Tokyo"
    volumes:
      - db-data:/var/lib/mysql
    ports:
      - 3306:3306
volumes:
  db-data:
104104

接続先情報を記載

spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://localhost:3306/app_db
spring.datasource.username=app_db
spring.datasource.password=secret
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#spring.jpa.show-sql: true
104104

flywayでマイグレーションを行う

1. build.gradleに依存関係を追加

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	developmentOnly 'org.springframework.boot:spring-boot-devtools'
	runtimeOnly 'com.mysql:mysql-connector-j'
	annotationProcessor 'org.projectlombok:lombok'
	providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	implementation 'org.flywaydb:flyway-core' //追記
}
  1. gradleではflywayが何故か正しく動作してくれなかったので、mavenでプロジェクトを作り直し。

pom.xmlのdependenciesにflyway-coreと今回はMySQLなのでflyway-mysqlを追記。pluginにもflyway関連のものを追記。

pom.xml
	<dependencies>
		<dependency>
			<groupId>org.flywaydb</groupId>
			<artifactId>flyway-core</artifactId>
		</dependency>
		<dependency>
			<groupId>org.flywaydb</groupId>
			<artifactId>flyway-mysql</artifactId>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.flywaydb</groupId>
				<artifactId>flyway-maven-plugin</artifactId>
				<configuration>
					<url>jdbc:mysql://localhost:3306/app_db</url>
					<user>app_db</user>
					<password>secret</password>
					<schemas>
						<schema>app_db</schema>
					</schemas>
				</configuration>
			</plugin>
		</plugins>
	</build>

src/main/resources/application.propertiesに以下を追記。

application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/app_db
spring.datasource.username=app_db
spring.datasource.password=secret
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.flyway.baseline-on-migrate=true
spring.flyway.enabled=true
spring.jpa.hibernate.ddl-auto=update
  1. src/main/resources/db/migrationディレクトリを作成し、V{バージョン番号}__{説明}.sqlの命名規則でsqlファイルを配置。とりあえず予約システムっぽいテーブルを作成。
V1__init.sql
CREATE TABLE IF NOT EXISTS `books` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `user_id` INT NOT NULL,
  `begin_dt` DATETIME NOT NULL,
  `end_dt` DATETIME NOT NULL,
  PRIMARY KEY (`id`))
ENGINE = InnoDB;

CREATE TABLE IF NOT EXISTS `users` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `last_name` VARCHAR(45) NOT NULL,
  `first_name` VARCHAR(45) NOT NULL,
  `sex` ENUM('male', 'femail') NULL,
  `birth` DATE NULL,
  PRIMARY KEY (`id`))
ENGINE = InnoDB;

CREATE TABLE IF NOT EXISTS `menus` (
  `id` INT NOT NULL,
  `name` VARCHAR(45) NULL,
  `default_time_slot` INT NULL,
  PRIMARY KEY (`id`))
ENGINE = InnoDB;

CREATE TABLE IF NOT EXISTS `book_menus` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `book_id` INT NOT NULL,
  `menu_id` INT NOT NULL,
  PRIMARY KEY (`id`))
ENGINE = InnoDB;
104104
  1. マイグレーションの実行

ターミナルからmaven経由でマイグレーションを実行。
mvnコマンドだとグローバルにmavenが実行できる状態でないと使えないが、プロジェクトルートのmvnwを叩けばそのまま実行できる。

> ./mvnw clean flyway:migrate
[INFO] Scanning for projects...
[INFO] 
[INFO] ------------------------< com.example:jar-demo >------------------------
[INFO] Building jar-demo 0.0.1-SNAPSHOT
[INFO]   from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- clean:3.3.2:clean (default-clean) @ jar-demo ---
[INFO] Deleting /xxx/workspace/jar-demo/target
[INFO] 
[INFO] --- flyway:9.22.3:migrate (default-cli) @ jar-demo ---
[INFO] Flyway Community Edition 9.22.3 by Redgate
[INFO] See release notes here: https://rd.gt/416ObMi
[INFO] 
[INFO] Database: jdbc:mysql://localhost:3306/app_db (MySQL 8.0)
[INFO] Successfully validated 1 migration (execution time 00:00.033s)
[INFO] Current version of schema `app_db`: 1
[INFO] Schema `app_db` is up to date. No migration necessary.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.087 s
[INFO] Finished at: 2023-12-31T21:22:06+09:00
[INFO] ------------------------------------------------------------------------

gradleの場合

./gradlew flywayMigrate
104104
  1. SpringBootの実行時に自動的にマイグレーションを走らせる

先ほどのpom.xmlにspring-boot-starter-data-jdbcの依存関係を追加する。

pom.xml
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jdbc</artifactId>
		</dependency>

あとは普通にSpringBootプロジェクトを立ち上げれば、マイグレーションが走る。

【参考】

java - Spring Boot Application is not running Flyway migrations on startup - Stack Overflow

java - Spring boot flyway migration not being triggered on application startup - Stack Overflow

104104

GAEにデプロイしてみる

コンソールからプロジェクトの作成

コンソールからGCP上でプロジェクトを作成し、

リージョンはus-centralのままでプロジェクト作成

104104

プロジェクト側でやること

  • gcloudコマンドを使う場合は、GAE側がソースコードからよしなにビルドしてくれるため、手動でjarファイルの出力は行わなくて良い。
  • mavenのコマンドでデプロイを行う場合はpom.xmlにGAE用のプラグインを入れる必要があるが、gcloudコマンドでデプロイする場合は不要。

app.ymlを配置

https://cloud.google.com/appengine/docs/standard/java-gen2/building-app/writing-web-service?hl=ja

こちらを参考に/src/main/appengine/ディレクトリの内にapp.ymlを作成する。
最小インスタンス数を0にしておかないと、ずっとインスタンスが立ち上がって無料枠を超えてしまうので注意する。

runtime: java17
env: standard

instance_class: F1

automatic_scaling:
  min_instances: 0
  max_instances: 2
  min_idle_instances: 0
  max_idle_instances: 2

自分がお試しに書いたのは上記のような感じ。

gcloud

> gcloud auth login
> gcloud config set project <projectID>
> gcloud app deploy
...
You can stream logs from the command line by running:
  $ gcloud app logs tail -s default

To view your application in the web browser run:
  $ gcloud app browse

初めてgcloudのコマンドを使う際にはログインとプロジェクトのセット(切り替え)を行う必要がある。

デプロイが完了したら gcloud app browseを叩くとブラウザで確認するためのURLを取得できるので、そちらのURLを確認する。

自分は最初503エラーでアクセスできなかったが、どうやらpom.xmlに記載している依存モジュールを上手く処理できなかったようで、エラーが出る場合はGCP管理画面のログ エクスプローラを確認して原因を特定する必要がある。

104104

Docker環境を作ってみる

やることとしては以下のようになる認識

  1. docker-composeを使う。
  2. アプリケーションコンテナのベースイメージはJDK。よく使われているのはOpenJDKっぽい。
  3. あらかじめtargetディレクトリにjarファイルを出力しておいて、コンテナを立ち上げたときにjarファイルをjavaコマンドで実行する。gradleであれば./gradlew clean buildで、mavenなら./mvnw clean packageを実行。
104104

Dockerfileを書く

FROM openjdk:17

WORKDIR /app

COPY target/your-spring-boot-app.jar /app/app.jar

EXPOSE 8080

CMD ["java", "-jar", "app.jar"]

オーソドックスなやり方だと上記のような感じ

104104
version: "3"

services:
  app:
    platform: linux/x86_64
    container_name: app
    image: maven:3.8.3-openjdk-17
    command: mvn spring-boot:run
    ports:
      - 8080:8080
    volumes:
      - ./src:/usr/src/spring-project
    working_dir: /usr/src/spring-project

.jarファイルを出力していない状態で走らせようとすると、maven(or gradle)が入ったイメージを使って上記のようになる。

https://stackoverflow.com/questions/59767129/docker-compose-for-local-spring-boot-development

104104

DB接続先

eclipceで動かしていたときとdocker-composeで動かすときではDBの接続先に注意する必要がある。

spring.datasource.url=jdbc:mysql://localhost:3306/app_db //eclipce + mysqlコンテナ
spring.datasource.url=jdbc:mysql://mysql:3306/app_db //docker-compose
104104

https://tech.excite.co.jp/entry/2021/12/19/145720

dev-toolを入れれば実現できるかと思っていた変更を即時に反映するホットロードは、そのままだと厳しそう。

https://ma-vericks.com/blog/spring-hotdeploy/

VSCodeの場合は上記手順を踏めばいけるのか。後ほど検証。
gradleとmavenの違いもあるらしくややこしい。

他、後で見る
https://stackoverflow.com/questions/59663235/spring-boot-live-reload-inside-a-docker-container-not-working

https://stackoverflow.com/questions/64902534/springboot-devtools-not-hot-reloading

このスクラップは2024/05/03にクローズされました