Spring Kafka反序列化报错:String无法转为Class问题求助
Spring Kafka ErrorHandlingDeserializer 配置引发 ClassCastException 问题
此前配置ErrorHandlingDeserializer构造参数时出现异常,后续又遇到新的类型转换错误,具体异常栈信息如下:
21:15:17.173 [main] WARN o.s.c.s.ClassPathXmlApplicationContext - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Failed to start bean 'containerListener' Exception in thread "main" org.springframework.context.ApplicationContextException: Failed to start bean 'containerListener' at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:186) at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:363) at java.base/java.lang.Iterable.forEach(Iterable.java:75) at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:160) at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:128) at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:968) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:618) at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:144) at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:85) at com.example.kafka.MyMainApp.main(MyMainApp.java:16) Caused by: java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Class (java.lang.String and java.lang.Class are in module java.base of loader 'bootstrap') at com.example.kafka.serdes.AppJsonDeserializer.configure(AppJsonDeserializer.java:35) at org.springframework.kafka.support.serializer.ErrorHandlingDeserializer.configure(ErrorHandlingDeserializer.java:137) at org.springframework.kafka.core.DefaultKafkaConsumerFactory.lambda$valueDeserializerSupplier$9(DefaultKafkaConsumerFactory.java:194) at org.springframework.kafka.core.DefaultKafkaConsumerFactory$ExtendedKafkaConsumer.<init>(DefaultKafkaConsumerFactory.java:479) at org.springframework.kafka.core.DefaultKafkaConsumerFactory.createRawConsumer(DefaultKafkaConsumerFactory.java:461) at org.springframework.kafka.core.DefaultKafkaConsumerFactory.createKafkaConsumer(DefaultKafkaConsumerFactory.java:438) at org.springframework.kafka.core.DefaultKafkaConsumerFactory.createConsumerWithAdjustedProperties(DefaultKafkaConsumerFactory.java:415) at org.springframework.kafka.core.DefaultKafkaConsumerFactory.createKafkaConsumer(DefaultKafkaConsumerFactory.java:382) at org.springframework.kafka.core.DefaultKafkaConsumerFactory.createConsumer(DefaultKafkaConsumerFactory.java:359) at org.springframework.kafka.listener.KafkaMessageListenerContainer$ListenerConsumer.<init>(KafkaMessageListenerContainer.java:890) at org.springframework.kafka.listener.KafkaMessageListenerContainer.doStart(KafkaMessageListenerContainer.java:393) at org.springframework.kafka.listener.AbstractMessageListenerContainer.start(AbstractMessageListenerContainer.java:510) at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:183) ... 9 more
对应的XML配置:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <bean id="employeeProducer" class="com.example.kafka.producer.EmployeeProducer" /> <util:map id="utilMap" map-class="java.util.HashMap"> <entry key="bootstrap.servers" value="localhost"/> <entry key="auto.offset.reset" value="latest"/> <entry key="group.id" value="group1" /> <entry key="client.id" value="my-client-id" /> <entry key="max.poll.records" value="1"/> <entry key="value.class.name" value="com.example.kafka.model.Employee" /> </util:map> <bean id="keyDeserializer" class="org.apache.kafka.common.serialization.StringDeserializer" /> <bean id="cf" class="org.springframework.kafka.core.DefaultKafkaConsumerFactory"> <constructor-arg index="0" ref="utilMap" /> <constructor-arg index="1" ref="keyDeserializer" /> <constructor-arg index="2" ref="valueDeserializer" /> <constructor-arg index="3" value="true" /> </bean> <bean id="valueDeserializer" class="org.springframework.kafka.support.serializer.ErrorHandlingDeserializer"> <constructor-arg name="delegate"> <bean class="com.example.kafka.serdes.AppJsonDeserializer"/> </constructor-arg> </bean> <bean id="cp" class="org.springframework.kafka.listener.ContainerProperties"> <constructor-arg name="topics" value="t-employee"/> <property name="groupId" value="group1"/> <property name="messageListener" ref="myListener"/> </bean> <bean id="containerListener" class="org.springframework.kafka.listener.KafkaMessageListenerContainer"> <constructor-arg index="0" ref="cf"/> <constructor-arg index="1" ref="cp" /> </bean> <bean id="myListener" class="com.example.kafka.listener.MyMessageListener" /> <bean id="employeeConsumer" class="com.example.kafka.consumer.EmployeeKafkaConsumer2" > <constructor-arg name="topic" value="t-employee" /> </bean> </beans>
问题分析
从异常栈可以看出,错误发生在AppJsonDeserializer.configure方法第35行:尝试将字符串类型的value.class.name配置值直接强转为Class对象,导致类型转换失败。因为在XML配置的utilMap中,value.class.name对应的是字符串形式的类全限定名,而非Class实例。
解决方案
方案1:修改自定义反序列化器的configure方法
在AppJsonDeserializer中,不要直接强转配置值,而是通过类名字符串加载对应的Class对象:
@Override public void configure(Map<String, ?> configs, boolean isKey) { String className = String.valueOf(configs.get("value.class.name")); try { this.targetClass = Class.forName(className); } catch (ClassNotFoundException e) { throw new IllegalArgumentException("Failed to load target class: " + className, e); } // 其他配置逻辑 }
方案2:直接为自定义反序列化器注入目标Class
无需在utilMap中配置value.class.name,改为在XML中直接给AppJsonDeserializer设置目标类:
<bean id="valueDeserializer" class="org.springframework.kafka.support.serializer.ErrorHandlingDeserializer"> <constructor-arg name="delegate"> <bean class="com.example.kafka.serdes.AppJsonDeserializer"> <property name="targetClass" value="com.example.kafka.model.Employee"/> </bean> </constructor-arg> </bean>
这种方式更直接,也避免了配置值的类型转换问题。
方案3:修改配置值的类型(不推荐)
如果一定要通过utilMap传递Class对象,可以将配置项改为Class类型,但Kafka原生配置通常以字符串为主,这种方式可能引发其他兼容性问题:
<util:map id="utilMap" map-class="java.util.HashMap"> <!-- 其他配置项 --> <entry key="value.class.name"> <value type="java.lang.Class">com.example.kafka.model.Employee</value> </entry> </util:map>
内容的提问来源于stack exchange,提问作者PAA
相关产品推荐
相关产品推荐

