Spring Boot Integration
Axon Framework provides extensive support for Spring Boot through its Spring Boot Starter extension. While Axon can be configured programmatically without Spring, the Spring Boot integration provides the easiest way to get started through automatic configuration.
The following table shows which Spring Boot versions are supported by each Axon Framework version:
| Axon Framework Version | Spring Boot 2 | Spring Boot 3 | Spring Boot 4 |
|---|---|---|---|
4.0 - 4.6 |
✓ |
||
4.7 - 4.12 |
✓ |
✓ |
|
4.13 |
✓ |
✓ |
✓ |
5.0.3+ |
✓ |
✓ |
Getting started
To enable Spring Boot auto-configuration, add the Axon Spring Boot Starter dependency to your project:
-
Maven
-
Gradle
<dependency>
<groupId>org.axonframework.extensions.spring</groupId>
<artifactId>axon-spring-boot-starter</artifactId>
<version>${axon.version}</version>
</dependency>
implementation 'org.axonframework.extensions.spring:axon-spring-boot-starter:${axonVersion}'
With this dependency in place, Axon will automatically:
-
Detect and register all message handlers (command handlers, event handlers, query handlers)
-
Configure the command bus, event bus, and query bus
-
Set up event processors to handle events
-
Configure entities and repositories
-
Wire all components together
Infrastructure configuration
The Spring Boot Starter will automatically configure the necessary infrastructure based on what’s available:
- Axon Server (recommended)
-
If the Axon Server connector is on the classpath, Axon will automatically connect to Axon Server (by default at
localhost:8124) and use it for:-
Command routing and distribution
-
Event storage and distribution
-
Query routing and distribution
-
You can disable Axon Server integration by setting axon.axonserver.enabled=false in your application properties.
- JPA fallback
-
If Axon Server is not available or disabled, Axon will use JPA-based implementations for Event storage.
- Extension-based implementations
-
Other extensions can provide additional implementations:
-
PostgreSQL event store (via the PostgreSQL extension)
-
Reactive gateways for commands, events, and queries (via the Reactor extension)
-
Component detection
The Spring Boot Starter automatically detects Axon components in your application:
-
Methods on Spring beans that are annotated with
@CommandHandler,@EventHandler, or@QueryHandlerare automatically registered -
Classes annotated with
@EventSourcedare registered as entities -
All detected components are wired with the appropriate buses and infrastructure
Customization
The auto-configuration is non-intrusive and only configures components that you haven’t explicitly defined.
You can override any auto-configured bean by defining your own in a @Configuration class.
For detailed configuration options, see the specific sections in this reference guide for commands, events, queries, and entities.
Event processor configuration
Spring Boot provides multiple approaches to configure event processors, ranging from simple properties-based configuration to programmatic type-safe configuration.
Configuration approaches
Properties-based configuration
The simplest approach for basic configuration using application.properties or application.yml:
axon.eventhandling.processors.my-processor.mode=pooled-streaming
axon.eventhandling.processors.my-processor.initial-segment-count=4
axon.eventhandling.processors.my-processor.batch-size=100
axon.eventhandling.processors.my-processor.thread-count=16
This approach is ideal for:
-
Simple processor configuration
-
Environment-specific settings (dev, test, prod)
-
Quick prototyping
Programmatic configuration with EventProcessorDefinition
For advanced configuration and type safety, use EventProcessorDefinition beans:
import org.axonframework.extension.spring.config.EventProcessorDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class EventProcessorConfig {
@Bean
public EventProcessorDefinition orderProcessor() {
return EventProcessorDefinition.pooledStreaming("order-processor")
.assigningHandlers(descriptor ->
descriptor.beanName().startsWith("order"))
.customized(config -> config
.initialSegmentCount(4)
.batchSize(100));
}
}
The EventProcessorDefinition API provides:
-
Type-safe configuration - Compile-time checking of configuration options
-
Fluent API - Chain configuration methods for readability
-
Handler selection - Define which event handlers belong to each processor using predicates
-
Full configuration access - All processor settings available programmatically
-
IDE support - Better autocompletion and refactoring support
Handler selection
The assigningHandlers() method provides access to an EventHandlerDescriptor with the following properties:
-
beanName()- The Spring bean name -
beanType()- The event handler class (use for package-based selection) -
beanDefinition()- The Spring bean definition -
component()- The component builder
This allows flexible handler assignment based on naming conventions, package structure, or custom criteria:
// By bean name prefix
.assigningHandlers(d -> d.beanName().startsWith("order"))
// By package
.assigningHandlers(d -> d.beanType().getPackageName().startsWith("com.example.orders"))
// By bean name pattern
.assigningHandlers(d -> d.beanName().contains("Handler"))
For easy matching of Event Handling Components to Event Processors purely based on the Event Processor name, you can use the @Namespace annotation in combination with the EventHandlerSelector.matchesNamespaceOnType().
The @Namespace annotation can be placed on the Event Handling Component, where the value of the annotation determines the Event Processor name it belongs to. Handlers will be automatically matched when defining an EventProcessorDefinition using the EventHandlerSelector.matchesNamespaceOnType() with the same name as defined in the @Namespace.
Let’s take a look at a concrete example:
import org.axonframework.extension.spring.config.EventHandlerSelector;
import org.axonframework.extension.spring.config.EventProcessorDefinition;
@Bean
public EventProcessorDefinition ordersProcessor() {
return EventProcessorDefinition.pooledStreaming("orders")
.assigningHandlers(EventHandlerSelector.matchesNamespaceOnType("orders"))
.notCustomized();
}
@Namespace("orders")
public class OrderEventHandler {
// omitted event handlers for brevity
}
With @Namespace on the OrderEventHandler, the OrderEventHandler signals it belongs to the Event Processor named orders. Note that the @Namespace annotation can be placed on a class (as shown above), on an enclosing class, on a package-info.java file, or on a module-info.java file. The @Namespace annotation is searched for in the order defined above.
-
On the type
-
On the enclosing class
-
On the
package-info.javafile -
On the
module-info.javafile
This allows you to use @Namespace to describe multiple event handlers belonging to a package, as shown below:
@Namespace("orders")
package com.example.orders;
import org.axonframework.messaging.core.annotation.Namespace;
|
Convenience
EventProcessorDefinition methodsFor the common case of matching handlers by namespace,
These methods use the processor name as the namespace to match, making it easy to organize processors by namespace or bounded context. |
Configuration options
Use customized() to customize processor settings, or notCustomized() when you only need handler assignment:
// With custom configuration
EventProcessorDefinition.pooledStreaming("custom-processor")
.assigningHandlers(descriptor -> descriptor.beanName().startsWith("custom"))
.customized(config -> config
.initialSegmentCount(16)
.batchSize(100)
.tokenClaimInterval(5000));
// With default settings (only handler assignment)
EventProcessorDefinition.pooledStreaming("default-processor")
.assigningHandlers(descriptor -> descriptor.beanName().startsWith("default"))
.notCustomized();
Configuration precedence
When using multiple configuration approaches:
-
EventProcessorDefinitionbeans take precedence over properties-based configuration -
If a handler matches multiple
EventProcessorDefinitionselectors, anAxonConfigurationExceptionis thrown at startup -
Handlers not matched by any
EventProcessorDefinitionare assigned based on their package name (default behavior)
To avoid conflicts, ensure each handler is matched by at most one EventProcessorDefinition using mutually exclusive selectors.
When to use each approach
Use properties-based configuration when:
-
Configuration is simple and doesn’t require custom logic
-
Settings may vary per environment
-
You’re prototyping or getting started
Use EventProcessorDefinition when:
-
You need fine-grained control over handler assignment
-
Configuration requires conditional logic
-
Type safety and IDE support are important
-
You want to share configuration between environments programmatically
For comprehensive event processor configuration examples and detailed information, see Event Processors.