はじめに
普段の業務ではEclipseで開発をしているのですが、ツールの制約によりEclipseが使えない場面に遭遇しました。
その時に代替案としてVS CodeでJava開発をしようとしたのですが、まったく歯が立たず悔しい想いをしました。
なので、今後VS CodeでJava開発をすることになっても大丈夫なように、デバッグ方法のキャッチアップをしてみます。
また、まだJavaのDockerコンテナを作ったことはなかったので、これを機にJavaのDockerコンテナも作ってみようかなと思います。
DevcontainerでSpring Bootを起動する
以下の作業は全てWSL上で行っています。
Spring Bootのサンプルコードを作成
2つのJavaファイルとpom.xmlを作成しました。
package com.example.springbootsample;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
package com.example.springbootsample;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/")
public String hello() {
return "Hello, Spring Boot!";
}
}
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>spring-boot-sample</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
これらのファイルを以下のように配置します。
$ tree -a
.
├── pom.xml
└── src
└── main
└── java
└── com
└── example
└── springbootsample
├── Application.java
└── HelloController.java
mvnwを生成
Mavenプロジェクトを実行するには予め以下を用意する必要があるみたいです。
- mvnw(スクリプトファイル)
- mvnw.cmd(Windows用スクリプトファイル)
- .mvn(設定ディレクトリ)
ローカルのプロジェクトルート以下のコマンドを実行します。
mvn -N io.takari:maven:wrapper
今回はたまたまWSL上にMavanやJavaのパスが通っていたので良かったですが、通っていない場合は事前に通す必要があります。
この作業すらもコンテナ内で出来ればいいな~と思いましたが、起動時に1回だけ必要な作業なのでDockerfileに書くのも微妙だな~と思ったり。。。悩ましい。
以下のようなディレクトリ構成でファイルやフォルダが生成されました。
$ tree -a
.
├── .mvn
│ └── wrapper
│ ├── MavenWrapperDownloader.java
│ ├── maven-wrapper.jar
│ └── maven-wrapper.properties
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
└── main
└── java
└── com
└── example
└── springbootsample
├── Application.java
└── HelloController.java
Dockerfileとdevcontainer.jsonを作成
以下のDockerfileとdevcontainer.jsonを作成しました。
# 基本となるイメージを指定
FROM eclipse-temurin:17-jdk
# 作業ディレクトリを作成
WORKDIR /app
# プロジェクトの全ファイルをコンテナ内にコピー
COPY . .
# Mavenを使ってプロジェクトのJARをビルド
COPY .mvn/ .mvn
COPY mvnw pom.xml ./
RUN ./mvnw dependency:go-offline
COPY src ./src
RUN ./mvnw package -DskipTests
# コンテナでアプリを実行
ENTRYPOINT ["java","-jar","target/spring-boot-sample-0.0.1-SNAPSHOT.jar"]
{
"name": "Java Dev Container",
"build": {
"dockerfile": "../Dockerfile"
},
"settings": {
"terminal.integrated.shell.linux": "/bin/bash"
},
"postCreateCommand": "./mvnw clean package"
}
配置はこんな感じです!
$ tree -a
.
├── .devcontainer
│ └── devcontainer.json
├── .mvn
│ └── wrapper
│ ├── MavenWrapperDownloader.java
│ ├── maven-wrapper.jar
│ └── maven-wrapper.properties
├── Dockerfile
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
└── main
└── java
└── com
└── example
└── springbootsample
├── Application.java
└── HelloController.java
余談ですが、最初はDockerfileを.devcontainer以下に配置していたのですが、途中からプロジェクトルートに置きました。
理由はDockerfile内でプロジェクト内のファイルを操作するときに、相対パス(../)を使用しなくはいけなくなり、管理が面倒だったからです。
DevcontainerでリモートアクセスしてSpring Bootを起動
VS Codeでコマンドパレットを開いてReopen container
を実行して、コンテナにリモートアクセスします。
その後、コンテナのプロジェクトルートで以下のコマンドを実行するとSpring BootのWebブラウザが立ち上がりました。
./mvnw spring-boot:run
VS Codeでデバッグする
拡張機能を入れる
JavaのデバッグをするためにExtension Pack for Java
を入れました。
Extension Pack for Java
は以下の7つの拡張機能をまとめたものみたいです。
- Maven for Java
- Debugger for Java
- Gradle for Java
- Project Manager for Java
- IntelliCode
- Test Runner for Java
- Language Support for Java(TM) by Red Hat
(上記のキャプチャには映ってないですTT)
devcontainer.jsonのextensions以下のように記載すると、コンテナへのリモートアクセスに自動的にインストールできます。
{
"name": "Java Dev Container",
"build": {
"dockerfile": "../Dockerfile"
},
"settings": {
"terminal.integrated.shell.linux": "/bin/bash"
},
"extensions": [
"vscjava.vscode-java-pack"
],
"postCreateCommand": "./mvnw clean package"
}
launch.jsonを作成する
launch.json
にJavaのデバッグ設定を記載するみたいです。
サイドバーのRun and Debugのタブを開きます。
Ctrl+Shift+Dでも開きます。
開いた後にcreate a launch.json fileをクリックして、debuggerでJavaを選択します。
launch.jsonが生成されます。
今回は自動的にCurrent File
とApplication
という2つのデバッグ設定が作成されました。
{
"version": "0.2.0",
"configurations": [
{
"type": "java",
"name": "Current File",
"request": "launch",
"mainClass": "${file}"
},
{
"type": "java",
"name": "Application",
"request": "launch",
"mainClass": "com.example.springbootsample.Application",
"projectName": "spring-boot-sample"
}
]
}
launch.jsonに記載したデバッグ設定はRun and Debugタブの上部で指定できるようになります。
いざデバッグ
上記で作成したApplication
をデバッグしてみます。
対象のクラスとなっているApplication.java
にprint文を足して、この行にブレイクポイントを止めてみました。
Run and Debugタブを開き対象をApplicationに設定して、その左にある実行ボタンを押してみるとデバッグが実行されました!
デバッグ用のコンソールも表示されましたし、ブレイクポイントも意図通りの場所に止まってる!!
この後はステップ実行などでデバッグを進めていく流れですね。
参考までに他のコマンドも記載しておきます。
- Continue:F5
- Stop:Shift + F5
- Step Over:F10
- Step Into:F11
- Step Out:Shift + F11
- Restart:Ctrl + Shift + F11
参考
ソースやDockerfileなどはChat GPTに作ってもらいました。
また下記のQiita記事も参考にさせていただきました。
おわりに
今回はJavaデバッグの練習として、シンプルなSpring Bootのプロジェクトを動かしてみました。
業務で行うレベルのデバッグには程遠いですが、基礎的なところはキャッチアップできたので、より詳細なデバッグができるようにもう少し勉強していきます!