# Getting Started with Spring Boot (/integrations/spring-boot/tutorials/getting-started)



The KubeMQ Spring Boot starter brings auto-configuration, a `KubeMQTemplate` for sending, and annotation-driven listeners to any Spring Boot application. This guide walks you from an empty project to a running app that publishes and receives events end-to-end against a local broker.

## Getting started [#getting-started]

<Steps>
  <Step>
    ### Prerequisites [#prerequisites]

    You need the following installed:

    | Requirement | Version                                 |
    | ----------- | --------------------------------------- |
    | Java        | 17+                                     |
    | Spring Boot | 3.2.0+                                  |
    | Docker      | Any recent version (for a local broker) |

    The starter talks to KubeMQ over gRPC only, so no HTTP connector or extra port is required for this guide.
  </Step>

  <Step>
    ### Start KubeMQ [#start-kubemq]

    Run KubeMQ in Docker. The gRPC port `50000` is the only port the Spring Boot starter uses:

    <RunKubeMQ ports="[50000]" />

    This matches the starter's default — `KubeMQProperties.address` is `localhost:50000` out of the box, so a broker on the local machine needs no extra configuration.

    <Callout type="info">
      Port `50000` is the native gRPC port used by all KubeMQ SDKs. The Spring Boot integration is built on the KubeMQ Java SDK, so it connects directly over gRPC rather than the shared HTTP server.
    </Callout>
  </Step>

  <Step>
    ### Add the Dependency [#add-the-dependency]

    Add the starter to your build. It is published under the `io.kubemq` group, version `1.0.0`.

    <Tabs groupId="build-tool" items="['Gradle', 'Maven']">
      <Tab value="Gradle">
        ```kotlin title="build.gradle.kts"
        dependencies {
            implementation("io.kubemq:kubemq-spring-boot-starter:1.0.0")
        }
        ```
      </Tab>

      <Tab value="Maven">
        ```xml title="pom.xml"
        <dependency>
            <groupId>io.kubemq</groupId>
            <artifactId>kubemq-spring-boot-starter</artifactId>
            <version>1.0.0</version>
        </dependency>
        ```
      </Tab>
    </Tabs>

    The starter is a dependency aggregator — adding it pulls in the auto-configuration module, the `KubeMQTemplate`, listener annotations, health, and metrics.
  </Step>

  <Step>
    ### Configure `application.yml` [#configure-applicationyml]

    Configure the broker address and a client identifier under the `kubemq` prefix. Because this example is a non-web app, also set `spring.main.web-application-type: none`:

    ```yaml title="src/main/resources/application.yml"
    spring:
      application:
        name: kubemq-example-events-basic-pubsub
      main:
        web-application-type: none

    kubemq:
      address: ${KUBEMQ_ADDRESS:localhost:50000}
      client-id: spring-events-basic-pubsub
    ```

    The `${KUBEMQ_ADDRESS:localhost:50000}` placeholder lets you override the broker address with an environment variable in other environments while defaulting to your local Docker broker. The `client-id` is sent with every request and identifies this application to the broker.
  </Step>

  <Step>
    ### Send a Message [#send-a-message]

    Inject `KubeMQTemplate` into any Spring bean and call `sendEvent(channel, payload)`. The template serializes the payload and publishes a fire-and-forget event:

    ```java title="OrderService.java"
    import io.kubemq.spring.boot.autoconfigure.template.KubeMQTemplate;
    import org.springframework.stereotype.Service;

    @Service
    public class OrderService {

        private final KubeMQTemplate template;

        public OrderService(KubeMQTemplate template) {
            this.template = template;
        }

        public void placeOrder(Order order) {
            template.sendEvent("orders", order);
        }
    }
    ```

    `sendEvent` is overloaded — you can pass a `Map<String, String>` of tags as a third argument, and `sendEventAsync` returns a `CompletableFuture<Void>` for non-blocking sends.
  </Step>

  <Step>
    ### Receive a Message [#receive-a-message]

    Annotate a method with `@KubeMQEventListener` to subscribe to one or more channels. The method receives an `EventMessageReceived`; call `getBody()` to read the raw payload bytes:

    ```java title="BasicPubSubListener.java"
    package io.kubemq.spring.boot.examples.events;

    import io.kubemq.sdk.pubsub.EventMessageReceived;
    import io.kubemq.spring.boot.autoconfigure.listener.KubeMQEventListener;
    import java.nio.charset.StandardCharsets;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;

    @Component
    public class BasicPubSubListener {

        private static final Logger log = LoggerFactory.getLogger(BasicPubSubListener.class);

        @KubeMQEventListener(channels = "spring-events.basic-pubsub")
        public void onEvent(EventMessageReceived event) {
            byte[] body = event.getBody();
            String text = body != null ? new String(body, StandardCharsets.UTF_8) : "<empty>";
            log.info("Received event: channel={} body={}", event.getChannel(), text);
        }
    }
    ```

    The `channels` attribute accepts one or more channel names and supports SpEL expressions and property placeholders (for example `channels = "${kubemq.channels.orders}"`).
  </Step>

  <Step>
    ### Run and Observe [#run-and-observe]

    Start your application. The listener subscribes on startup, the publisher sends three events to `spring-events.basic-pubsub`, and both sides log to the console. You should see interleaved `Published` and `Received` lines:

    ```text
    INFO  Received event: channel=spring-events.basic-pubsub body=Hello KubeMQ #1
    INFO  Published event to spring-events.basic-pubsub: Hello KubeMQ #1
    INFO  Received event: channel=spring-events.basic-pubsub body=Hello KubeMQ #2
    INFO  Published event to spring-events.basic-pubsub: Hello KubeMQ #2
    INFO  Received event: channel=spring-events.basic-pubsub body=Hello KubeMQ #3
    INFO  Published event to spring-events.basic-pubsub: Hello KubeMQ #3
    INFO  Basic pub/sub example completed.
    ```

    <Callout type="info">
      Events are fire-and-forget: the listener must be subscribed before a message is sent, otherwise that message is not delivered. The example inserts a short warm-up delay before publishing so the subscription is established first.
    </Callout>
  </Step>

  <Step>
    ### Java vs Kotlin [#java-vs-kotlin]

    The starter is fully usable from Java with YAML configuration. The optional `kubemq-spring-boot-starter-kotlin` module adds a configuration DSL and coroutine-friendly `suspend` extensions.

    <Tabs groupId="spring-lang" items="['Java', 'Kotlin']">
      <Tab value="Java">
        Configure with `application.yml` and send with the blocking `sendEvent`:

        ```java title="OrderService.java"
        @Service
        public class OrderService {

            private final KubeMQTemplate template;

            public OrderService(KubeMQTemplate template) {
                this.template = template;
            }

            public void placeOrder(Order order) {
                template.sendEvent("orders", order);
            }
        }
        ```
      </Tab>

      <Tab value="Kotlin">
        Configure programmatically with the `kubemq { }` DSL by exposing it as a bean:

        ```kotlin title="KubeMQConfig.kt"
        import io.kubemq.spring.boot.kotlin.KubeMQConfigurerDsl
        import io.kubemq.spring.boot.kotlin.kubemq
        import org.springframework.context.annotation.Bean
        import org.springframework.context.annotation.Configuration

        @Configuration
        class KubeMQConfig {

            @Bean
            fun kubemqConfigurer(): KubeMQConfigurerDsl = kubemq {
                address = "localhost:50000"
                clientId = "spring-kotlin-dsl-config"
            }
        }
        ```

        Send from a coroutine with the `sendEventSuspend` extension, which suspends until the underlying async send completes:

        ```kotlin title="CoroutinePublishRunner.kt"
        import io.kubemq.spring.boot.autoconfigure.template.KubeMQTemplate
        import io.kubemq.spring.boot.kotlin.sendEventSuspend
        import kotlinx.coroutines.runBlocking
        import org.springframework.boot.ApplicationArguments
        import org.springframework.boot.ApplicationRunner
        import org.springframework.stereotype.Component

        @Component
        class CoroutinePublishRunner(private val template: KubeMQTemplate) : ApplicationRunner {

            override fun run(args: ApplicationArguments) {
                runBlocking {
                    template.sendEventSuspend("spring-kotlin.coroutine-publish", "Coroutine event")
                }
            }
        }
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step>
    ### Next Steps [#next-steps]

    <Cards>
      <Card title="Concepts" href="/integrations/spring-boot/concepts" description="Understand auto-configuration, the KubeMQTemplate, and listener annotations." />

      <Card title="Events & Events Store" href="/integrations/spring-boot/how-to/events-and-events-store" description="Use fire-and-forget events and persistent events store with replay." />

      <Card title="Reference" href="/integrations/spring-boot/reference/configuration" description="Complete kubemq.* configuration properties and API reference." />
    </Cards>
  </Step>
</Steps>
