---
title: "Run a Java worker with Spring Boot"
description: "Run a dedicated Spring worker service with discovered stage beans, managed lifecycle and execution scopes."
category: "Java SDK"
---

The Spring Boot starter binds `application.yml`, discovers stage beans, starts the configured transport and stops the worker with the application context. Deploy this application as a dedicated worker service. Producer applications remain separate and do not import the worker's stage beans into their Spring context.

[Download the Spring demo](/downloads/pipelogiq-java-spring-demo.zip). It uses the same `Order`, `ValidateOrder` and `RecordOrder` business classes as [getting started](/docs/java/getting-started), plus a Spring entry point.

## Install the starter

From the matching SDK source root:

```sh
./mvnw -B -pl sdk-spring-boot-starter -am install -DskipTests
```

Use this dependency instead of the plain application's entry dependency; it includes the SDK runtime:

```xml
<dependency>
  <groupId>com.pipelogiq</groupId>
  <artifactId>pipelogiq-sdk-spring-boot-starter</artifactId>
  <version>0.5.0</version>
</dependency>
```

The starter targets Spring Boot 3 and Java 17+. The downloadable application contains a complete POM, so it can be compiled with the matching SDK's Maven wrapper without assuming Central publication.

## Configure the application

Create `src/main/resources/application.yml`:

```yaml
spring:
  main:
    keep-alive: true

pipelogiq:
  api-url: ${PIPELOGIQ_API_URL:http://localhost:8081}
  api-key: ${PIPELOGIQ_API_KEY}
  worker-name: ${PIPELOGIQ_WORKER_NAME:orders-java-spring}
  environment: development
  transport: ${PIPELOGIQ_TRANSPORT:http}
  max-concurrency: 4
```

`spring.main.keep-alive: true` keeps this non-web worker process alive after application startup. The starter manages its startup and shutdown; keep the process running continuously like any other worker service.

These settings use Spring's property sources, profiles and placeholder resolution. They do not invoke the standalone YAML file loader. Explicit placeholders let this sample use the same `PIPELOGIQ_*` environment names as the plain demo. See [configuration](/docs/java/configuration) for timeouts and transport options.

## Register the business stages as beans

`src/main/java/example/spring/SpringOrdersApplication.java`:

```java
package example.spring;

import example.RecordOrder;
import example.ValidateOrder;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;

@SpringBootApplication
@Import({ValidateOrder.class, RecordOrder.class})
public class SpringOrdersApplication {
  public static void main(String[] args) {
    SpringApplication.run(SpringOrdersApplication.class, args);
  }
}
```

`@Import` turns these existing classes into beans without adding Spring annotations to the business stages. Alternatively, use `@Component` scanning or declare `@Bean` factory methods. Keep `@Stage` for stable wire names and implement `StageHandler<T>` or `NoInputStageHandler` as usual.

The starter publishes typed `PipelogiqClient` and low-level `PipelogiqApiClient` beans when the application has not provided its own. In this worker service, stages can borrow the typed client through `StageContext` when they need to create related work. A separate producer-only Spring application can use the starter with no stage beans; then no worker is created.

## Build, run and submit

From the extracted `pipelogiq-java-spring-demo` directory, with `PIPELOGIQ_SDK_DIR` pointing to the SDK source root:

```sh
"$PIPELOGIQ_SDK_DIR/mvnw" -q compile dependency:copy-dependencies
export PIPELOGIQ_API_URL='http://localhost:8081'
export PIPELOGIQ_API_KEY='your-application-api-key'
java -cp 'target/classes:target/dependency/*' example.spring.SpringOrdersApplication
```

Stop any basic demo worker first if you want this Spring worker to execute the submitted order. Keep the Spring process running. In another terminal, open the same demo, set the API settings, and submit:

```sh
java -cp 'target/classes:target/dependency/*' example.CreatePipelineExample submit spring-001 42.50
java -cp 'target/classes:target/dependency/*' example.CreatePipelineExample wait 123 60
```

Replace `123` with the printed ID. Expect `COMPLETED`, `Recorded: true` and two completed stages. Ctrl+C closes the Spring context and drains the worker. Set `PIPELOGIQ_TRANSPORT=amqp` before starting to exercise the same application over AMQP.

## Use the client in a separate Spring producer

A different Spring application, such as an HTTP API, can receive the application-scoped client through constructor injection. Do not register stage beans in that producer application. Share DTOs and stable `StageRef` contracts instead of depending on worker implementation classes:

```java
package example.spring;

import example.Order;
import io.pipelogiq.sdk.core.PipelogiqClient;
import io.pipelogiq.sdk.core.client.PipelineResponse;
import io.pipelogiq.sdk.core.client.StageRef;
import org.springframework.stereotype.Service;

@Service
public final class OrderSubmissionService {
  private static final StageRef<Order> VALIDATE = StageRef.of("ValidateOrder", Order.class);
  private static final StageRef<Void> RECORD = StageRef.noInput("RecordOrder");
  private final PipelogiqClient client;

  public OrderSubmissionService(PipelogiqClient client) {
    this.client = client;
  }

  public PipelineResponse submit(Order order) {
    return client.pipeline("Process order " + order.orderId())
        .idempotencyKey("spring-order-" + order.orderId())
        .stage(VALIDATE, order)
        .stage(RECORD)
        .send();
  }
}
```

This class belongs to the producer application, not the worker service shown above. Do not close the injected client after each request; Spring owns it. Use named async operations when the surrounding application needs `CompletableFuture` composition, while keeping the bean alive for their duration.

## Choose a stage lifetime

| Bean lifetime | Behavior |
| --- | --- |
| Singleton, the Spring default | Borrowed stage instance; may execute concurrently and must be thread-safe |
| Prototype stage | New handler for an invocation; the starter destroys that handler afterward |
| `pipelogiq-stage` scope | Per-invocation stage/dependency graph with destruction callbacks |

For an invocation-scoped stage or dependency, use:

```java
import io.pipelogiq.sdk.spring.PipelogiqStageScope;
import org.springframework.context.annotation.Scope;

@Scope(PipelogiqStageScope.NAME)
```

Place the annotation on the corresponding bean class or `@Bean` factory method. Scope both the stage and the dependencies that must share its attempt lifetime. A prototype handler alone does not automatically give every dependency managed per-attempt cleanup. Borrowed singleton dependencies remain application-owned.

## Control startup in tests

Set `pipelogiq.auto-startup: false` to create the managed worker without connecting automatically. A test can then inspect its configuration and beans without requiring a live server. This is not a production hosting mode and does not replace transport integration tests.

Do not write your own shutdown hook in a Spring application. Let the context stop the worker before its dependent resources are closed. A closed worker handle cannot be restarted; create a new application context or worker for another lifetime.
