Keywords: Spring Boot | Axon Framework | Version Compatibility | Classpath Conflict | Maven Dependency Management
Abstract: This article provides an in-depth analysis of common version compatibility issues when integrating the Axon framework into Spring Boot projects, focusing on classpath conflicts caused by multiple incompatible versions, particularly the JpaEventStorageEngine initialization error. Through a practical case study, it explains the root causes, troubleshooting steps, and solutions, emphasizing best practices in Maven dependency management to ensure a single, compatible Axon version. Code examples and configuration adjustments are included to help developers avoid similar problems.
Problem Background and Error Symptoms
In development with Spring Boot and the Axon framework, version compatibility is a common yet often overlooked issue. This article explores a specific case where upgrading Axon dependencies leads to classpath conflict errors. When using Spring Boot 2.0.6.RELEASE, a user attempted to upgrade Axon from version 3.0-RC1 to 3.4, resulting in the following critical error upon application startup:
***************************
APPLICATION FAILED TO START
***************************
Description:
An attempt was made to call the method org.axonframework.eventsourcing.eventstore.jpa.JpaEventStorageEngine.<init>(Lorg/axonframework/common/jpa/EntityManagerProvider;)V but it does not exist. Its class, org.axonframework.eventsourcing.eventstore.jpa.JpaEventStorageEngine, is available from the following locations:
jar:file:/C:/Users/user/.m2/repository/org/axonframework/axon-core/3.4/axon-core-3.4.jar!/org/axonframework/eventsourcing/eventstore/jpa/JpaEventStorageEngine.class
It was loaded from the following location:
file:/C:/Users/user/.m2/repository/org/axonframework/axon-core/3.4/axon-core-3.4.jar
Action:
Correct the classpath of your application so that it contains a single, compatible version of org.axonframework.eventsourcing.eventstore.jpa.JpaEventStorageEngine
The error message clearly indicates that the application attempts to call the constructor of the JpaEventStorageEngine class, but the method does not exist. Although the class file can be loaded from axon-core-3.4.jar, version incompatibility leads to method signature mismatches. This typically occurs when the classpath contains multiple versions of Axon dependencies or conflicts between dependency versions.
Root Cause Analysis
By analyzing the user's pom.xml configuration file, the core issue is identified as inconsistent dependency versions. The relevant configuration is as follows:
<dependency>
<groupId>org.axonframework</groupId>
<artifactId>axon-spring-boot-starter</artifactId>
<version>3.0-RC1</version>
</dependency>
<dependency>
<groupId>org.axonframework</groupId>
<artifactId>axon-amqp</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>org.axonframework</groupId>
<artifactId>axon-core</artifactId>
<version>3.4</version>
</dependency>
There is an obvious version conflict: axon-spring-boot-starter uses version 3.0-RC1, while axon-amqp and axon-core use version 3.4. During version upgrades in the Axon framework, APIs and internal implementations may change, especially from RC (Release Candidate) to stable releases. Between 3.0-RC1 and 3.4, the constructor signature of the JpaEventStorageEngine class might have been modified, causing the runtime to fail to locate the corresponding method.
More deeply, axon-spring-boot-starter is an aggregate dependency that may internally reference specific versions of axon-core and other modules. When different versions of axon-core are explicitly declared, Maven's dependency resolution mechanism can result in two versions of axon-core on the classpath, leading to conflicts. As Spring Boot loads classes at startup, if multiple incompatible versions are detected, it throws the aforementioned error.
Solutions and Best Practices
Based on the best answer, the solution is to unify all Axon dependency versions. Specifically, change the version of axon-spring-boot-starter from 3.0-RC1 to 3.4 to match other dependencies. The modified configuration is as follows:
<dependency>
<groupId>org.axonframework</groupId>
<artifactId>axon-spring-boot-starter</artifactId>
<version>3.4</version>
</dependency>
This adjustment ensures that the classpath contains only a single, compatible version of Axon, eliminating version conflicts. In practice, developers should adhere to the following best practices:
- Version Consistency: Ensure all related dependencies use the same version number in Maven or Gradle projects. This can be managed centrally by defining properties, for example, in
pom.xml:
<properties>
<axon.version>3.4</axon.version>
</properties>
<dependency>
<groupId>org.axonframework</groupId>
<artifactId>axon-spring-boot-starter</artifactId>
<version>${axon.version}</version>
</dependency>
<dependency>
<groupId>org.axonframework</groupId>
<artifactId>axon-core</artifactId>
<version>${axon.version}</version>
</dependency>
<ol start="2">
<exclusions> tag to exclude them, then explicitly declare the correct version.mvn dependency:tree command to analyze the dependency tree and identify conflict sources.Code Examples and In-Depth Understanding
To illustrate the issue more clearly, here is a simplified Spring Boot application example demonstrating proper Axon dependency configuration. Assume we have an aggregate root handling user commands:
import org.axonframework.commandhandling.CommandHandler;
import org.axonframework.spring.stereotype.Aggregate;
import javax.persistence.Entity;
import java.util.UUID;
@Aggregate
@Entity
public class UserAggregate {
private String userId;
private String name;
public UserAggregate() {
// Default constructor
}
@CommandHandler
public UserAggregate(CreateUserCommand command) {
apply(new UserCreatedEvent(UUID.randomUUID().toString(), command.getName()));
}
// Other event handling methods and business logic
}
In application.properties, configure JPA and Axon settings:
spring.datasource.url=jdbc:hsqldb:mem:testdb
spring.datasource.driverClassName=org.hsqldb.jdbcDriver
spring.jpa.hibernate.ddl-auto=create-drop
axon.eventhandling.processors.default.mode=tracking
axon.serializer.general=jackson
By unifying dependency versions, the application can start smoothly, leveraging Axon's event sourcing mechanism to persist events to JPA storage. For instance, when sending a CreateUserCommand, Axon invokes JpaEventStorageEngine to save the UserCreatedEvent without initialization failures due to version conflicts.
Conclusion and Extended Considerations
This article provides a detailed analysis of version compatibility issues in integrating Spring Boot with the Axon framework through a practical case study. The key is to ensure the classpath contains only a single, compatible dependency version, avoiding runtime errors caused by API changes. Developers should prioritize dependency management, regularly update and test dependency versions to leverage new features and fixes. Additionally, in microservices architectures, consider using BOM (Bill of Materials) or dependency management plugins to simplify version control. By following these practices, project stability and maintainability can be enhanced, reducing development obstacles from classpath conflicts.