Questi contenuti non sono ancora disponibili nella tua lingua.
This article is the reference for the Aspire Java hosting integration. It enumerates the AppHost APIs — with examples for both apphost.mts and AppHost.cs — that you use to model Java apps in your AppHost: Spring Boot and Quarkus apps with framework-aware defaults, apps that launch through a Maven goal or a Gradle task, prebuilt JARs, and container images that were built elsewhere. It also describes how Aspire builds those apps, passes them JVM options, attaches the OpenTelemetry Java agent, configures certificate trust, debugs them, and packages them into container images when you publish.
Use AddSpringBootAppaddSpringBootApp to add a Spring Boot app whose Maven or Gradle build file is in the app directory. The app directory is resolved relative to the AppHost directory. It’s the app’s working directory and, when you publish, the build context of its container image.
Detects the build tool from the app directory: pom.xml selects Maven, and build.gradle, build.gradle.kts, settings.gradle, or settings.gradle.kts selects Gradle. If the directory contains neither — or both — the app fails to start with an error. For apps that are laid out differently, see Add a Java app.
Launches the app through the project’s wrapper with the Spring Boot plugin’s run goal: spring-boot:run for Maven, or bootRun for Gradle.
Adds an HTTP endpoint named http and passes its port to the app in the SERVER_PORT environment variable, which Spring Boot uses as its server.port setting. No fixed port is set, so several Spring Boot apps can run side by side.
The helper doesn’t add a health check, because /actuator/health responds only when the app depends on spring-boot-starter-actuator. A health check against a missing endpoint would leave the app permanently unhealthy and block every resource that waits for it.
When you publish, the app is packaged inside its container image with -B -ntp -DskipTests package for Maven or build -x test for Gradle. To change those arguments, see Build before running.
Use AddQuarkusAppaddQuarkusApp to add a Quarkus app. The build tool is detected the same way as for Spring Boot apps. The app runs in Quarkus dev mode — quarkus:dev for Maven or quarkusDev for Gradle — so live coding keeps working while the AppHost runs, and its HTTP endpoint’s port reaches the app in the QUARKUS_HTTP_PORT environment variable.
var builder =DistributedApplication.CreateBuilder(args);
var pricing =builder.AddQuarkusApp("pricing","../pricing")
.WithHttpHealthCheck("/q/health");
builder.AddQuarkusApp("inventory","../inventory")
.WithReference(pricing)
.WaitFor(pricing);
builder.Build().Run();
When you run the AppHost, the helper also sets the following environment variables on the app:
Environment variable
Value
Purpose
QUARKUS_PROFILE
dev
Selects the dev profile, including when a debugger launches the packaged app instead of dev mode.
QUARKUS_HTTP_HOST
0.0.0.0
Binds all interfaces. When Quarkus binds a localhost name, its Host header validation rejects requests to the endpoint URL that the Aspire dashboard shows with 400 Bad Request.
QUARKUS_OBSERVABILITY_ENABLED
false
Turns off the observability Dev Service, which would otherwise pull and start a grafana/otel-lgtm container and send the app’s telemetry there instead of to the Aspire dashboard.
The Quarkus OpenTelemetry extension reads its own quarkus.otel.* configuration rather than the standard OTEL_* environment variables that Aspire sets. So that the app’s telemetry reaches the dashboard, the helper copies Aspire’s OpenTelemetry settings to the matching QUARKUS_OTEL_* variables, such as QUARKUS_OTEL_EXPORTER_OTLP_ENDPOINT and QUARKUS_OTEL_SERVICE_NAME. A Quarkus app that uses the extension doesn’t need the OpenTelemetry Java agent, so don’t attach both.
The helper copies these settings only when you run the AppHost. A published app receives the standard OTEL_* variables from its deployment environment, so map them in the app’s application.properties:
Quarkus expands these expressions when the app starts. The endpoint and protocol defaults match the extension’s own, and the service name falls back to the app’s name, so the app also runs outside Aspire. Keep the expressions in application.properties rather than passing them as environment variables, because Docker Compose interprets ${...} in environment variables itself and rejects this syntax.
Other Quarkus Dev Services stay enabled. A Dev Service starts its own container only when the configuration it provides is missing. When your app references an Aspire resource, such as a database, map the connection information that Aspire injects to the matching Quarkus property — for example, quarkus.datasource.jdbc.url — and Quarkus uses the Aspire resource instead. For examples, see Connect to Java apps.
As with Spring Boot, no health check is added, because /q/health responds only when the app depends on the quarkus-smallrye-health extension. When you publish, Quarkus apps are packaged with the same default arguments as Spring Boot apps.
java -jar with the JAR path and the app’s arguments
An app without a launch mode fails to start. Mixing launch modes — a JAR with a Maven goal or Gradle task, or Maven with Gradle — throws an exception when you configure it. Calling the same method again replaces the earlier goal or task.
Arguments that you pass with the goal or task go to the build tool. In TypeScript, the arguments array is required, so pass [] when there are none. Arguments that you add with WithArgswithArgs are also placed after the goal or task, so in these launch modes they reach the build tool rather than your app. To pass arguments to the app itself, use the build plugin’s option for it, such as -Dspring-boot.run.arguments=... for the Spring Boot Maven plugin or --args=... for the Spring Boot Gradle plugin.
Unlike the framework helpers, AddJavaApp doesn’t declare an endpoint. Add one with WithHttpEndpointwithHttpEndpoint, and use its env parameter to pass the port that Aspire allocates in the environment variable that your app reads, as the preceding example does with SERVER_PORT. Leave the target port unset: Java apps run as processes on your machine, so two apps with the same fixed port would collide.
Pass a JAR path to the JAR overload of AddJavaApp to run the app with java -jar. The JAR path is relative to the app directory, and the remaining arguments are the app’s own arguments, which Aspire passes after the JAR path. In TypeScript, this overload is named addJavaAppWithJar, and its arguments array is required.
var builder =DistributedApplication.CreateBuilder(args);
builder.AddJavaApp(
"worker",
"../worker",
"target/worker.jar",
"--interval-seconds","10");
builder.Build().Run();
The JAR must exist before the app starts. When the project’s own build produces it, have Aspire run that build first, as described in Build before running. When you publish, the JAR path selects which of the build’s JARs the image runs. For more information, see Select the published JAR.
Use WithMavenBuildwithMavenBuild or WithGradleBuildwithGradleBuild to build an app with its wrapper before it starts — typically an app that runs a JAR its own build produces. When you run the AppHost, Aspire adds a child resource named <name>-maven-build or <name>-gradle-build that runs the build, and the app waits for that resource to finish successfully. When you publish, the same arguments build the app inside its generated container image, so they should produce the deployable JAR.
By default, Maven builds with clean package and Gradle builds with clean build. Arguments that you pass replace the defaults, so include the packaging goal or task. An app is built with one tool only: combining the Maven and Gradle methods on the same app throws an exception.
A Maven goal or Gradle task compiles the app as it launches. For apps that use one — including apps added with AddSpringBootApp or AddQuarkusApp — Aspire keeps the build arguments for publishing without adding a build resource. Two cases still build first: an app whose OpenTelemetry agent the build produces, and a Quarkus app that VS Code launches for debugging. On a Spring Boot or Quarkus app, calling WithMavenBuild or WithGradleBuild replaces the default packaging arguments and keeps the framework’s launch goal.
Aspire builds and launches apps only through the project’s wrapper — mvnw or gradlew, or mvnw.cmd or gradlew.bat on Windows — so that the AppHost, your CI, and your published images all use the tool version that the repository pins. It doesn’t fall back to a globally installed mvn or gradle.
Aspire looks for the wrapper in the app directory first. If the wrapper isn’t there, Aspire checks each parent directory up to the repository root, and uses a wrapper that sits next to the root build file of a multi-module build: a Maven pom.xml, or a Gradle settings.gradle or settings.gradle.kts. If there’s no wrapper, the app fails to start with an error that suggests generating one with mvn -N wrapper:wrapper or gradle wrapper.
To use a wrapper in another location or with another name, call WithWrapperPathwithWrapperPath. The path is absolute, or relative to the app’s working directory — the app directory, unless you change it. You can call the method before or after the one that selects the build tool.
var builder =DistributedApplication.CreateBuilder(args);
builder.AddJavaApp("catalog","../catalog")
.WithWrapperPath("tools/mvnw")
.WithMavenGoal("spring-boot:run");
builder.Build().Run();
When Aspire generates the Dockerfile to publish the app, the wrapper must be inside the app directory, because that directory is the container build context. This applies even when a local run finds the wrapper in a parent directory. For more information, see Publish Java apps.
Use WithJvmArgswithJvmArgs to pass options to the JVM, such as heap sizes or system properties. Aspire appends them to the JAVA_TOOL_OPTIONS environment variable, and quotes values that contain spaces. Every JVM reads that variable when it starts — including the build tool’s own JVM and the app JVM it starts in a Maven goal or Gradle task — so the options apply in every launch mode, and to Java containers as well.
Aspire also uses JAVA_TOOL_OPTIONS for the OpenTelemetry Java agent and the development certificate trust store, and adds your options alongside those. When a JVM reads the variable, it prints a Picked up JAVA_TOOL_OPTIONS: line to standard error, so that line appears in the app’s console logs.
Aspire sets the standard OTEL_* environment variables on every Java resource, such as OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_SERVICE_NAME, so that telemetry can reach the Aspire dashboard. A JVM doesn’t export telemetry by itself, though. To produce traces, metrics, and logs without changing your code, attach the OpenTelemetry Java agent, which instruments common libraries and frameworks automatically and reads those variables. Aspire doesn’t download the agent — your build provides it, or you point Aspire at a copy that already exists:
Without an argument, WithOtelAgent()withOtelAgentDefaultPath() loads the agent from where the build writes it by convention: target/agent/opentelemetry-javaagent.jar for Maven, or build/agent/opentelemetry-javaagent.jar for Gradle. Aspire needs to know the build tool, so use it on an app added with AddSpringBootApp, or on one that calls WithMavenBuild or WithGradleBuild.
With a path, WithOtelAgent(string agentPath)withOtelAgent(agentPath: string) loads the agent from that path. A relative path is resolved against the app directory and names a file that the build produces. An absolute path names a file that already exists, such as an agent installed on the machine, and doesn’t need a build.
var builder =DistributedApplication.CreateBuilder(args);
builder.AddSpringBootApp("catalog","../catalog")
.WithOtelAgent();
builder.Build().Run();
When the build produces the agent, Aspire runs that build as a <name>-maven-build or <name>-gradle-build resource before the app starts — even for a Maven goal or Gradle task that would otherwise compile the app itself. The agent has to exist first, because JAVA_TOOL_OPTIONS loads it into the build tool’s own JVM too. Calling the method again replaces the agent path.
The following build configuration copies the agent to the conventional location. Replace the version with the latest agent release:
A separate configuration keeps the agent off the app’s classpath, and the bootRun and classes tasks depend on the copy so that the agent exists before the app starts.
When you publish with a generated Dockerfile, Aspire copies a build-produced agent into the image and points the JVM at that copy. An absolute agent path is used unchanged, so the agent must already exist at that path inside the image. If you author your own Dockerfile, copy the agent in that Dockerfile and pass its absolute path inside the image, because Aspire copies a build-produced agent only into Dockerfiles that it generates.
Use AddJavaContaineraddJavaContainer when a Java app ships as a container image that’s built elsewhere, such as by a separate pipeline or another team. Aspire runs the image as-is and never rebuilds it, so the JAR, the JDK, and any agent all come from the image. The resource still receives the OpenTelemetry exporter settings and the development certificate trust store, and it supports WithJvmArgs.
Aspire doesn’t add an endpoint to a Java container, because the port that the app listens on is a property of the image. Declare it with WithHttpEndpoint and a target port — 8080 is the default for both Spring Boot and Quarkus.
The OpenTelemetry agent methods aren’t available on a Java container, because there’s no build to take the agent from. If the image already includes the agent, enable it with a JVM option that points to its location inside the image, such as -javaagent:/app/opentelemetry-javaagent.jar.
Aspire configures each Java resource to trust the development certificate, so HTTPS calls to other local resources succeed without turning off certificate validation. It creates a PKCS12 trust store that contains the development certificate together with the system’s root certificates, and points the JVM at it by adding -Djavax.net.ssl.trustStore and -Djavax.net.ssl.trustStoreType=PKCS12 to JAVA_TOOL_OPTIONS.
A trust store that’s passed to the JVM replaces the JVM’s default trusted certificates rather than adding to them, so the default trust scope for Java resources is System. If you change a Java resource’s scope to Append with WithCertificateTrustScopewithCertificateTrustScope, Aspire can’t add the certificate without replacing the defaults, so it leaves the JVM’s trust store unchanged and logs a message.
When the debugger launches a Java app, VS Code starts the app’s JVM directly instead of going through the wrapper, because goals such as spring-boot:run and bootRun start a second JVM that a debugger attached to the wrapper would never see. Aspire drops the launch goal or task and its arguments for that launch, and passes the app’s environment variables — including JAVA_TOOL_OPTIONS — and the arguments that you add with WithArgs to the debugger. Settings that you pass only through goal or task arguments, such as -Dspring-boot.run.profiles=dev, don’t apply to a debug launch, so use an environment variable, such as SPRING_PROFILES_ACTIVE, for settings that should apply to both.
The debugger needs the app’s main class. Aspire reads it from the JAR that the project’s most recent build left in its output directory: the Start-Class that the Spring Boot plugins record, or a plain JAR’s Main-Class. If there’s no such JAR, the Java debugger looks for the main class in the project, and asks you to choose one when it finds more than one. To choose the main class in the AppHost instead, call WithMainClasswithMainClass, which affects only launches from the IDE:
Sets the main class an IDE launches when running or debugging this application. Has no effect on how Aspire starts the process, which is decided by the JAR path, Maven goal, or Gradle task.
For a prebuilt JAR, the debugger puts the JAR on the classpath and launches the Main-Class from its manifest, as java -jar does, unless you choose another class with WithMainClass. A Quarkus app is launched from the fast JAR that its build packages (quarkus-app/quarkus-run.jar in the build output), so when VS Code launches a Quarkus app, Aspire runs the app’s build first. Breakpoints in your source still bind.
When you run aspire publish or aspire deploy, Aspire packages each Java app as a container image. If the app directory contains a Dockerfile, Aspire uses it as-is. Otherwise, Aspire generates a multi-stage Dockerfile that:
Builds the project inside the image with the project’s wrapper and the arguments from WithMavenBuild or WithGradleBuild. Without either method, it packages the app with -B -ntp -DskipTests package for Maven or -x test build for Gradle. Dependency caches are kept in a BuildKit cache mount rather than in an image layer.
Detects the Java version that the project targets from pom.xml or the Gradle build file, defaulting to Java 21, and runs the app on the matching eclipse-temurin JRE image. The build stage uses the matching JDK image, unless the Maven or Gradle version that the wrapper pins can’t run on that JDK, in which case it uses the nearest JDK version that the tool supports.
Runs the app as an unprivileged user (USER 999:999) with the JVM as process ID 1, so the JVM receives SIGTERM directly and shutdown hooks run.
The app directory is the build context, so everything that the build needs must be inside it, including the wrapper when Aspire generates the Dockerfile. For the same reason, changing a Java app’s working directory after you add it isn’t supported when Aspire generates the Dockerfile. Java containers that you add with AddJavaContainer keep using the image that you supply.
A JAR path doesn’t by itself mean that the image copies a prebuilt JAR. When the app directory contains a Maven or Gradle build file — or the app has a build step, Maven goal, or Gradle task — the image builds the project, and Aspire selects the JAR to run from the build output in this order:
For a Quarkus app, the artifact that the build’s quarkus-artifact.properties file names, together with the dependencies that it needs. All three Quarkus packaging types — fast-jar, legacy-jar, and uber-jar — work without extra configuration.
The JAR path that you passed to AddJavaApp.
The only JAR that the build produced, ignoring -plain, -sources, and -javadoc JARs.
If the last step finds more than one candidate — such as the original-*.jar that a shade plugin leaves beside the shaded JAR — the image build fails and lists the candidates. Name the right one with WithJarArtifact. The method affects only publishing, so an app can run one JAR locally and publish another.
Only an app directory without a build file, for an app without a build step, Maven goal, or Gradle task, publishes by copying the prebuilt JAR into the image. Build output directories usually aren’t in source control, so building inside the image avoids publishing a stale local JAR, and still works in a clean clone or on a CI agent.
The generated Dockerfile builds on docker.io/library/eclipse-temurin:<version>-jdk and runs on docker.io/library/eclipse-temurin:<version>-jre. The build stage needs only a JDK, because the project’s wrapper downloads the Maven or Gradle version that it pins. To use other images, such as mirrors in your own registry, set both in a single call to WithDockerfileBaseImagewithDockerfileBaseImage:
The generated Dockerfile can copy files that another resource produces, such as a frontend’s build output, into the Java app’s image. Call PublishWithContainerFilespublishWithContainerFiles on the Java app with the source resource and a destination path. A relative destination path is resolved against the image’s /app working directory, so the following example places the frontend app’s build output in /app/static:
Aspire builds the source resource before it builds the Java app’s image. Copying the files doesn’t make the app serve them, so configure the app to serve static files from that directory. Aspire adds the files only to the Dockerfile that it generates, so an authored Dockerfile in the app directory doesn’t receive them.
The Java hosting integration doesn’t add health checks to Java resources. When your app exposes a health endpoint, such as /actuator/health with Spring Boot Actuator or /q/health with SmallRye Health, add a check with WithHttpHealthCheck.
Remove CommunityToolkit.Aspire.Hosting.Java from the AppHost’s package references, #:package directives, or aspire.config.json. Don’t keep both packages, because they define overlapping APIs.
Add Aspire.Hosting.Java, as described in Installation.
Update your AppHost code with the replacements in the following table. The table uses C# names. TypeScript AppHosts use the same names in camel case, except that the JAR overload of AddJavaApp is addJavaAppWithJar, and the parameterless WithOtelAgent is withOtelAgentDefaultPath.
Review how your Java apps are published. The first-party integration builds a container image for each app that you add with AddJavaApp, AddSpringBootApp, or AddQuarkusApp, from the app’s own Dockerfile or from one that Aspire generates. For more information, see Publish Java apps.
Community Toolkit API
First-party replacement
AddJavaContainerApp
AddJavaContainer. In TypeScript, pass the image tag as { tag: '...' }.
AddSpringApp, and overloads that take JavaAppExecutableResourceOptions or JavaAppContainerResourceOptions
AddSpringBootApp, AddJavaApp, or AddJavaContainer, configured with builder methods.
The workingDirectory parameter of AddJavaApp
The appDirectory parameter.
JavaAppExecutableResource and JavaAppContainerResource
JavaAppResource and JavaContainerResource.
The JarPath resource property
Pass the JAR path when you add the app, and use WithJarArtifact to choose the published JAR.
WithMavenBuild(MavenOptions)
WithMavenBuild with the build arguments, including the packaging goal. For a custom Command, also call WithWrapperPath.
WithOtelAgent(), called only to configure telemetry export
Remove the call, because every Java resource is configured for export. The parameterless method now attaches the agent that the build produces.