使用Akka Persistence出现JournalFailureException恢复异常如何解决?
Added Event Let's break down this issue and fix it step by step—this is a common gotcha with Akka Persistence and Jackson serialization.
The Root Cause
Your error message points straight to the problem:
Cannot construct instance of com.exercisePersistence.simpleExample.MyPersistentBehavior$Added (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
This happens for two key reasons:
- The
Addedevent is likely a non-static inner class ofMyPersistentBehavior—Jackson can’t instantiate non-static inner classes without an instance of the outer class, which it doesn’t have during recovery. - Jackson lacks the metadata to map the JSON payload to your
Addedclass’s constructor parameters.
Step 1: Convert Added to a Static Inner Class
Non-static inner classes depend on an outer class instance to exist, which breaks Jackson’s deserialization workflow. Update your event class to be static:
public class MyPersistentBehavior extends EventSourcedBehavior<MyPersistentBehavior.Command, MyPersistentBehavior.Event, MyPersistentBehavior.State> { // COMMAND interface Command {} public static class Add implements Command { public final String data; public Add(String data) { this.data = data; } } // EVENT - Make Added a STATIC inner class interface Event {} public static class Added implements Event { public final String data; public Added(String data) { this.data = data; } } // ... rest of your Behavior logic ... }
Step 2: Give Jackson Constructor Mapping Metadata
Even with a static class, Jackson needs help to connect the JSON data field to your constructor parameter. Pick one of these options:
Option A: Annotate the Constructor Manually
Use Jackson’s @JsonProperty to explicitly map the parameter to the JSON field:
import com.fasterxml.jackson.annotation.JsonProperty; public static class Added implements Event { public final String data; public Added(@JsonProperty("data") String data) { this.data = data; } }
Option B: Use Lombok (If You’re Already Using It)
If you use Lombok, simplify this with annotations that generate Jackson-compatible code automatically:
import lombok.Value; // @Value generates a final class, all-args constructor, and getters // Lombok adds implicit @JsonProperty annotations to constructor parameters in recent versions @Value public static class Added implements Event { String data; }
Step 3: Optional: Simplify Serialization Configuration
Your existing reference.conf bindings are valid, but you can make serialization more maintainable by having your Event interface extend Akka’s JacksonSerializable. This lets Akka automatically use Jackson for all events without explicit class bindings:
import akka.serialization.jackson.JacksonSerializable; interface Event extends JacksonSerializable {}
Why This Fixes the Issue
- Static inner classes don’t require an outer class instance, so Jackson can instantiate them directly during recovery.
@JsonPropertyor Lombok annotations give Jackson the blueprint it needs to turn the JSON{"data":"data1"}payload into a validAddedinstance.
Reference Context
For clarity, here’s the original error stack trace you shared:
Caused by: akka.persistence.typed.internal.JournalFailureException: Exception during recovery. Last known sequence number [0]. PersistenceId [PersistenceExample], due to: Cannot construct instance of com.exercisePersistence.simpleExample.MyPersistentBehavior$Added (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator) at [Source: (byte[]){"data":"data1"}; line: 1, column: 2] at akka.persistence.typed.internal.ReplayingEvents.onRecoveryFailure(ReplayingEvents.scala:221) at akka.persistence.typed.internal.ReplayingEvents.onJournalResponse(ReplayingEvents.scala:143) ... 26 common frames omitted Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of com.exercisePersistence.simpleExample.MyPersistentBehavior$Added (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator) at [Source: (byte[]){"data":"data1"}; line: 1, column: 2]
And your existing HOCON configuration (which works correctly once the class fixes are applied):
akka { actor { serializers { jackson-json = "akka.serialization.jackson.JacksonJsonSerializer" jackson-cbor = "akka.serialization.jackson.JacksonCborSerializer" proto = "akka.remote.serialization.ProtobufSerializer" } serialization-bindings { "com.exercisePersistence.simpleExample.MyPersistentBehavior" = jackson-json "com.exercisePersistence.simpleExample.MyPersistentBehavior$Added" = jackson-json #"docs.serialization.CborSerializable" = jackson-cbor "com.google.protobuf.Message" = proto } } akka.serialization.jackson { jackson-json-message { serialization-features { WRITE_DATES_AS_TIMESTAMPS = on } } jackson-json-event { serialization-features { WRITE_DATES_AS_TIMESTAMPS = off } } } persistence { journal { plugin = "leveldb" leveldb { dir = "target/persistence/journal" } } snapshot-store.local.dir = "target/persistence/snapshots" } } leveldb { dir = "target/persistence/journal" checksum: "off" class: "akka.persistence.journal.leveldb.LeveldbJournal" dir: "target/persistence/journal" fsync: "on" native: "on" plugin-dispatcher : "akka.persistence.dispatchers.default-plugin-dispatcher" replay-dispatcher : "akka.persistence.dispatchers.default-replay-dispatcher" }
内容的提问来源于stack exchange,提问作者AM13

