使用java.util.List作为Avro数组类型,Kafka反序列化报错求助
NoSuchMethodException: java.util.List.<init>() 我的场景
我配置了如下Kafka消费者属性来读取Avro格式的消息:
value.deserializer=io.confluent.kafka.serializers.KafkaAvroDeserializer key.deserializer=org.apache.kafka.common.serialization.StringDeserializer specific.avro.reader=true
对应的Avro Schema定义如下:
{ "type" : "array", "items" : { "type" : "record", "name" : "MyDto", "namespace" : "test.dto", "fields" : [ { "default" : null, "name" : "version", "type" : ["null","string"] }, { "default" : null, "name" : "testName", "type" : ["null","string"] }, { "name" : "keys", "type" : {"type": "array", "items": "string"}, "java-class" : "java.util.List" } ] }, "java-class" : "java.util.List" }
我已经通过这个Schema成功将对象写入Kafka,但在反序列化时抛出了以下异常:
java.lang.NoSuchMethodException: java.util.List.<init>()
想请教一下:我是否可以在Schema中指定java.util.List作为目标类型?当前我使用的是Confluent 3.1.2版本。
问题原因与解决方案
这个问题的核心在于**java.util.List是一个接口,没有无参构造函数**,而Confluent 3.1.2版本的KafkaAvroDeserializer在处理java-class配置时,需要一个可以直接实例化的具体类——它会尝试调用该类的无参构造方法来创建实例,接口显然无法满足这个要求。
具体解决步骤:
修改Schema中的
java-class配置:
将Schema里两处"java-class" : "java.util.List"替换为具体的实现类,比如java.util.ArrayList:{ "type" : "array", "items" : { "type" : "record", "name" : "MyDto", "namespace" : "test.dto", "fields" : [ { "default" : null, "name" : "version", "type" : ["null","string"] }, { "default" : null, "name" : "testName", "type" : ["null","string"] }, { "name" : "keys", "type" : {"type": "array", "items": "string"}, "java-class" : "java.util.ArrayList" } ] }, "java-class" : "java.util.ArrayList" }或者你可以直接去掉
java-class配置,因为Avro默认会把array类型映射为java.util.ArrayList,不需要额外指定。代码中仍可使用List接收:
即使Schema里指定的是ArrayList,你在业务代码中完全可以用List<MyDto>或者List<String>来接收反序列化后的对象——因为ArrayList是List的实现类,向上转型完全没问题,不影响你的代码抽象。
补充说明:
Confluent 3.1.2对应的Avro版本相对较旧(大概是Avro 1.7.x系列),这个版本的Avro序列化框架对接口类型的支持有限。在后续的新版本中,虽然对接口的处理有优化,但从最佳实践来说,Schema中还是应该指定具体的可实例化类,避免这类反射实例化的问题。
内容的提问来源于stack exchange,提问作者Katya Gorshkova

