使用容器進行 Java 開發

先決條件

按照步驟操作,將您的應用程式容器化,請參閱 將您的應用程式容器化

概觀

在本節中,您將逐步設定您在上一節中容器化的應用程式的本地開發環境。 這包括

  • 新增本地資料庫並保存資料
  • 建立開發容器以連接偵錯器
  • 設定 Compose 以在您編輯和儲存程式碼時自動更新正在執行的 Compose 服務

新增本地資料庫並保存資料

您可以使用容器來設定本地服務,例如資料庫。在本節中,您將更新 `docker-compose.yaml` 檔案來定義資料庫服務和保存資料的卷。此外,此特定應用程式使用系統屬性來定義資料庫類型,因此您需要更新 `Dockerfile` 以在啟動應用程式時傳入系統屬性。

在複製的儲存庫目錄中,使用 IDE 或文字編輯器開啟 `docker-compose.yaml` 檔案。您的 Compose 檔案有一個範例資料庫服務,但它需要針對您的應用程式進行一些更改。

在 `docker-compose.yaml` 檔案中,您需要執行以下操作

  • 取消註釋所有資料庫指令。您現在將使用資料庫服務而不是本地儲存來儲存資料。
  • 移除頂層 `secrets` 元素以及 `db` 服務內的元素。此範例使用環境變數作為密碼,而不是 secrets。
  • 從 `db` 服務中移除 `user` 元素。此範例在環境變數中指定使用者。
  • 更新資料庫環境變數。這些是由 Postgres 影像定義的。如需詳細資訊,請參閱 Postgres 官方 Docker 影像
  • 更新 `db` 服務的健康檢查測試並指定使用者。預設情況下,健康檢查使用 root 使用者而不是您定義的 `petclinic` 使用者。
  • 在 `server` 服務中將資料庫 URL 新增為環境變數。這會覆蓋在 `spring-petclinic/src/main/resources/application-postgres.properties` 中定義的預設值。

以下是更新後的 `docker-compose.yaml` 檔案。所有註釋都已移除。

services:
  server:
    build:
      context: .
    ports:
      - 8080:8080
    depends_on:
      db:
        condition: service_healthy
    environment:
      - POSTGRES_URL=jdbc:postgresql://db:5432/petclinic
  db:
    image: postgres
    restart: always
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=petclinic
      - POSTGRES_USER=petclinic
      - POSTGRES_PASSWORD=petclinic
    ports:
      - 5432:5432
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "petclinic"]
      interval: 10s
      timeout: 5s
      retries: 5
volumes:
  db-data:

在 IDE 或文字編輯器中開啟 `Dockerfile`。在 `ENTRYPOINT` 指令中,更新指令以傳入 `spring-petclinic/src/resources/db/postgres/petclinic_db_setup_postgres.txt` 檔案中指定的系統屬性。

- ENTRYPOINT [ "java", "org.springframework.boot.loader.launch.JarLauncher" ]
+ ENTRYPOINT [ "java", "-Dspring.profiles.active=postgres", "org.springframework.boot.loader.launch.JarLauncher" ]

儲存並關閉所有檔案。

現在,執行以下 `docker compose up` 命令來啟動您的應用程式。

$ docker compose up --build

開啟瀏覽器並在 http://localhost:8080用於開發的 Dockerfile

您現在擁有的 Dockerfile 非常適合小型、安全的生產環境映像,其中僅包含執行應用程式所需的組件。開發時,您可能需要具有不同環境的不同映像。

例如,在開發映像中,您可能想要設定映像以啟動應用程式,以便您可以將偵錯器連線到正在執行的 Java 程序。

您可以新增一個新的階段,而不是管理多個 Dockerfile。然後,您的 Dockerfile 可以產生可供生產的最終映像以及開發映像。

將 Dockerfile 的內容替換為以下內容。

# syntax=docker/dockerfile:1

FROM eclipse-temurin:21-jdk-jammy as deps
WORKDIR /build
COPY --chmod=0755 mvnw mvnw
COPY .mvn/ .mvn/
RUN --mount=type=bind,source=pom.xml,target=pom.xml \
    --mount=type=cache,target=/root/.m2 ./mvnw dependency:go-offline -DskipTests

FROM deps as package
WORKDIR /build
COPY ./src src/
RUN --mount=type=bind,source=pom.xml,target=pom.xml \
    --mount=type=cache,target=/root/.m2 \
    ./mvnw package -DskipTests && \
    mv target/$(./mvnw help:evaluate -Dexpression=project.artifactId -q -DforceStdout)-$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout).jar target/app.jar

FROM package as extract
WORKDIR /build
RUN java -Djarmode=layertools -jar target/app.jar extract --destination target/extracted

FROM extract as development
WORKDIR /build
RUN cp -r /build/target/extracted/dependencies/. ./
RUN cp -r /build/target/extracted/spring-boot-loader/. ./
RUN cp -r /build/target/extracted/snapshot-dependencies/. ./
RUN cp -r /build/target/extracted/application/. ./
ENV JAVA_TOOL_OPTIONS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8000
CMD [ "java", "-Dspring.profiles.active=postgres", "org.springframework.boot.loader.launch.JarLauncher" ]

FROM eclipse-temurin:21-jre-jammy AS final
ARG UID=10001
RUN adduser \
    --disabled-password \
    --gecos "" \
    --home "/nonexistent" \
    --shell "/sbin/nologin" \
    --no-create-home \
    --uid "${UID}" \
    appuser
USER appuser
COPY --from=extract build/target/extracted/dependencies/ ./
COPY --from=extract build/target/extracted/spring-boot-loader/ ./
COPY --from=extract build/target/extracted/snapshot-dependencies/ ./
COPY --from=extract build/target/extracted/application/ ./
EXPOSE 8080
ENTRYPOINT [ "java", "-Dspring.profiles.active=postgres", "org.springframework.boot.loader.launch.JarLauncher" ]

儲存並關閉 `Dockerfile`。

在 Dockerfile 中,您新增了一個基於 `extract` 階段的名為 `development` 的新階段。在此階段,您將提取的檔案複製到一個通用目錄,然後執行命令來啟動應用程式。在命令中,您公開埠 8000 並宣告 JVM 的偵錯設定,以便您可以附加偵錯器。

使用 Compose 進行本地開發

目前的 Compose 檔案不會啟動您的開發容器。為此,您必須更新 Compose 檔案以指向開發階段。此外,更新伺服器服務的埠映射以提供對偵錯器的存取。

開啟 `docker-compose.yaml` 並將以下指令新增到檔案中。

services:
  server:
    build:
      context: .
      target: development
    ports:
      - 8080:8080
      - 8000:8000
    depends_on:
      db:
        condition: service_healthy
    environment:
      - POSTGRES_URL=jdbc:postgresql://db:5432/petclinic
  db:
    image: postgres
    restart: always
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=petclinic
      - POSTGRES_USER=petclinic
      - POSTGRES_PASSWORD=petclinic
    ports:
      - 5432:5432
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "petclinic"]
      interval: 10s
      timeout: 5s
      retries: 5
volumes:
  db-data:

現在,啟動您的應用程式並確認它正在執行。

$ docker compose up --build

最後,測試您的 API 端點。執行以下 curl 命令

$ curl  --request GET \
  --url http://localhost:8080/vets \
  --header 'content-type: application/json'

您應該收到以下回應

{
  "vetList": [
    {
      "id": 1,
      "firstName": "James",
      "lastName": "Carter",
      "specialties": [],
      "nrOfSpecialties": 0,
      "new": false
    },
    {
      "id": 2,
      "firstName": "Helen",
      "lastName": "Leary",
      "specialties": [{ "id": 1, "name": "radiology", "new": false }],
      "nrOfSpecialties": 1,
      "new": false
    },
    {
      "id": 3,
      "firstName": "Linda",
      "lastName": "Douglas",
      "specialties": [
        { "id": 3, "name": "dentistry", "new": false },
        { "id": 2, "name": "surgery", "new": false }
      ],
      "nrOfSpecialties": 2,
      "new": false
    },
    {
      "id": 4,
      "firstName": "Rafael",
      "lastName": "Ortega",
      "specialties": [{ "id": 2, "name": "surgery", "new": false }],
      "nrOfSpecialties": 1,
      "new": false
    },
    {
      "id": 5,
      "firstName": "Henry",
      "lastName": "Stevens",
      "specialties": [{ "id": 1, "name": "radiology", "new": false }],
      "nrOfSpecialties": 1,
      "new": false
    },
    {
      "id": 6,
      "firstName": "Sharon",
      "lastName": "Jenkins",
      "specialties": [],
      "nrOfSpecialties": 0,
      "new": false
    }
  ]
}

連接偵錯器

您將使用 IntelliJ IDEA 附帶的偵錯器。您可以使用此 IDE 的社群版本。在 IntelliJ IDEA 中開啟您的專案,前往**執行**選單,然後**編輯設定**。新增一個新的遠端 JVM 偵錯設定,類似於以下內容

Java Connect a Debugger

設定斷點。

開啟 `src/main/java/org/springframework/samples/petclinic/vet/VetController.java` 並在 `showResourcesVetList` 函式內新增一個斷點。

要啟動偵錯階段,請選擇**執行**選單,然後**偵錯*您的設定名稱***。

Debug menu

您現在應該會在 Compose 應用程式的日誌中看到連線。

Compose log file

您現在可以呼叫伺服器端點。

$ curl --request GET --url http://localhost:8080/vets

您應該已經看到程式碼在標記的行上中斷,現在您可以像往常一樣使用偵錯器。您還可以檢查和監看變數、設定條件斷點、檢視堆疊追蹤以及執行其他操作。

Debugger code breakpoint

在終端機中按 `ctrl+c` 以停止您的應用程式。

自動更新服務

使用 Compose Watch 在您編輯和儲存程式碼時自動更新正在執行的 Compose 服務。如需 Compose Watch 的詳細資訊,請參閱 使用 Compose Watch

在 IDE 或文字編輯器中開啟您的 `docker-compose.yaml` 檔案,然後新增 Compose Watch 指令。以下是更新後的 `docker-compose.yaml` 檔案。

services:
  server:
    build:
      context: .
      target: development
    ports:
      - 8080:8080
      - 8000:8000
    depends_on:
      db:
        condition: service_healthy
    environment:
      - POSTGRES_URL=jdbc:postgresql://db:5432/petclinic
    develop:
      watch:
        - action: rebuild
          path: .
  db:
    image: postgres
    restart: always
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=petclinic
      - POSTGRES_USER=petclinic
      - POSTGRES_PASSWORD=petclinic
    ports:
      - 5432:5432
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "petclinic"]
      interval: 10s
      timeout: 5s
      retries: 5
volumes:
  db-data:

執行以下命令以使用 Compose Watch 執行您的應用程式。

$ docker compose watch

開啟網路瀏覽器並在 http://localhost:8080

儲存對 layout.html 的變更,然後您可以在容器自動重建時繼續開發。

在容器重建並執行後,重新整理 http://localhost:8080 ,然後確認選單中現在顯示的是 **首頁!**。

在終端機中按下 ctrl+c 以停止 Compose Watch。

摘要

在本節中,您了解了如何在本地執行資料庫並保存資料。您還建立了一個包含 JDK 的開發映像檔,讓您可以附加偵錯器。最後,您設定了 Compose 檔案以公開偵錯埠,並設定了 Compose Watch 以即時重新載入您的變更。

相關資訊

後續步驟

在下一節中,您將了解如何在 Docker 中執行單元測試。