Scala Akka微服务集群Jackson序列化报Cannot construct instance of Logger异常
根本原因
报错核心原因是org.slf4j.Logger是抽象接口,没有默认构造方法,本身也不应该被序列化传输:Logger是节点本地的日志组件实例,和当前节点的日志配置、运行上下文绑定,跨节点传输Logger实例没有实际意义,就算序列化成功,在目标节点也无法正常工作。
推荐解决方案
以下方案按优先级从高到低排列:
方案1:伴生对象托管Logger(最优,符合Scala最佳实践)
Logger属于类级别的资源,不需要每个case class实例单独持有,放在伴生对象中即可全局复用,且伴生对象的属性不会参与case class实例的序列化流程,从根源避免问题。
代码示例:case class MyCaseClassMessage() extends CborSerializable { val businessLogic: Array[Byte] = ??? def apply(): Array[Byte] = ??? } object MyCaseClassMessage { val LOG: org.slf4j.Logger = org.slf4j.LoggerFactory.getLogger(classOf[MyCaseClassMessage]) }业务代码中需要打日志时,直接调用
MyCaseClassMessage.LOG即可,不影响原有逻辑。方案2:添加@JsonIgnore注解忽略字段
如果不想调整现有代码结构,直接在LOG字段上添加Jackson的字段忽略注解即可:
代码示例:import com.fasterxml.jackson.annotation.JsonIgnore case class MyCaseClassMessage() extends CborSerializable { @JsonIgnore override val LOG: org.slf4j.Logger = org.slf4j.LoggerFactory.getLogger(classOf[MyCaseClassMessage]) val businessLogic: Array[Byte] = ??? def apply(): Array[Byte] = ??? }方案3:全局配置Jackson忽略Logger类型
如果项目中大量消息类都声明了Logger字段,不想逐个修改,可以自定义Jackson Module全局忽略所有org.slf4j.Logger类型的字段:
第一步:实现自定义Jackson Moduleimport com.fasterxml.jackson.databind.module.SimpleModule class LoggerIgnoreModule extends SimpleModule { override def setupModule(context: SimpleModule.SetupContext): Unit = { context.configOverride(classOf[org.slf4j.Logger]) .setIsIgnoredType(true) } }第二步:在
application.conf中注册Module到Akka的Jackson序列化器akka.serialization.jackson { modules += "com.your.custom.package.LoggerIgnoreModule" }
注意事项
不要尝试为Logger实现自定义序列化/反序列化器,这是无效方案:序列化后的Logger实例传输到目标节点后,和目标节点的日志上下文、配置不匹配,无法正常输出日志,属于无用操作。
内容的提问来源于stack exchange,提问作者Heitor Santana

