Java 8 date/time type `java.time.LocalDateTime` not supported by default: add Module “

Caused by: org.springframework.messaging.converter.MessageConversionException: Could not write JSON: Java 8 date/time type `java.time.LocalDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (through reference chain: com.ir.mq.model.message.receive.GenerateDutyMQMessage["createTime"]); nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type `java.time.LocalDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (through reference chain: com.ir.mq.model.message.receive.GenerateDutyMQMessage["createTime"])
	at org.springframework.messaging.converter.MappingJackson2MessageConverter.convertToInternal(MappingJackson2MessageConverter.java:273)
	at org.springframework.messaging.converter.AbstractMessageConverter.toMessage(AbstractMessageConverter.java:201)
	at org.springframework.messaging.converter.CompositeMessageConverter.toMessage(CompositeMessageConverter.java:96)
	at org.springframework.messaging.core.AbstractMessageSendingTemplate.doConvert(AbstractMessageSendingTemplate.java:181)
	at org.apache.rocketmq.spring.core.RocketMQTemplate.doConvert(RocketMQTemplate.java:985)
	at org.apache.rocketmq.spring.core.RocketMQTemplate.createRocketMqMessage(RocketMQTemplate.java:1025)
	at org.apache.rocketmq.spring.core.RocketMQTemplate.sendAndReceive(RocketMQTemplate.java:238)
	... 115 common frames omitted
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type `java.time.LocalDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (through reference chain: com.ir.mq.model.message.receive.GenerateDutyMQMessage["createTime"])
	at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77)
	at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1276)
	at com.fasterxml.jackson.databind.ser.impl.UnsupportedTypeSerializer.serialize(UnsupportedTypeSerializer.java:35)
	at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:728)
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:770)
	at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:178)
	at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480)
	at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:319)
	at com.fasterxml.jackson.databind.ObjectMapper.writeValue(ObjectMapper.java:3126)
	at org.springframework.messaging.converter.MappingJackson2MessageConverter.convertToInternal(MappingJackson2MessageConverter.java:255)
	... 121 common frames omitted

springboot 对象转json报错,原因很简单jackson不支持LocalDateTime需要额外引入依赖

<dependency>
	<groupId>com.fasterxml.jackson.datatype</groupId>
	<artifactId>jackson-datatype-jsr310</artifactId>
	<version>2.13.0</version>
</dependency>

引入后依旧报错。

源码跟踪

com.fasterxml.jackson.databind.ser.BeanSerializerFactory#_findUnsupportedTypeSerializer
    protected JsonSerializer<?> _findUnsupportedTypeSerializer(SerializerProvider ctxt,
            JavaType type, BeanDescription beanDesc)
        throws JsonMappingException
    {
        // 05-May-2020, tatu: Should we check for possible Shape override to "POJO"?
        //   (to let users force 'serialize-as-POJO'?
        final String errorMsg = BeanUtil.checkUnsupportedType(type);
        if (errorMsg != null) {
            // 30-Sep-2020, tatu: [databind#2867] Avoid checks if there is a mix-in
            //    which likely providers a handler...
            if (ctxt.getConfig().findMixInClassFor(type.getRawClass()) == null) {
                return new UnsupportedTypeSerializer(type, errorMsg);
            }
        }
        return null;
    }

在BeanSerializerFactory这个类中获取JsonSerializer时会检查不支持的类。

com.fasterxml.jackson.databind.util.BeanUtil#checkUnsupportedType
   public static String checkUnsupportedType(JavaType type) {
        final String className = type.getRawClass().getName();
        String typeName, moduleName;

        if (isJava8TimeClass(className)) {
            // [modules-java8#207]: do NOT check/block helper types in sub-packages,
            // but only main-level types (to avoid issues with module)
            if (className.indexOf('.', 10) >= 0) {
                return null;
            }
            typeName =  "Java 8 date/time";
            moduleName = "com.fasterxml.jackson.datatype:jackson-datatype-jsr310";
        } else if (isJodaTimeClass(className)) {
            typeName =  "Joda date/time";
            moduleName = "com.fasterxml.jackson.datatype:jackson-datatype-joda";
        } else {
            return null;
        }
        return String.format("%s type %s not supported by default: add Module \"%s\" to enable handling",
                typeName, ClassUtil.getTypeDescription(type), moduleName);
    }

从源码上面看Java8的date/time和joda的date/time是不支持的。

回到com.fasterxml.jackson.databind.ser.BeanSerializerFactory#_findUnsupportedTypeSerializer方法中,如果存在错误errorMsg,则会去SerializerProvider类的SerializationConfig配置中寻找_mixIns

。。。。。后面就不知道怎么玩了。手动哭笑

然后继续百度,在GitHub的问题中找到了思路。

Springboot 2.5.0 - Regression - Swagger3 RuntimeException: Could not write JSON · Issue #3829 · springfox/springfox · GitHub

其中有这句话Tried opening a SpringBoot issue, but it seems this is internal springfox issue, as explained to me here:

然后想到我其他实体类中的LocalDateTime是能正常转换的。于是开始看rocketMQ的json配置源码,

@Configuration
@ConditionalOnMissingBean(RocketMQMessageConverter.class)
class MessageConverterConfiguration {

    @Bean
    public RocketMQMessageConverter createRocketMQMessageConverter() {
        return new RocketMQMessageConverter();
    }

}


public RocketMQMessageConverter() {
        List<MessageConverter> messageConverters = new ArrayList<>();
        ByteArrayMessageConverter byteArrayMessageConverter = new ByteArrayMessageConverter();
        byteArrayMessageConverter.setContentTypeResolver(null);
        messageConverters.add(byteArrayMessageConverter);
        messageConverters.add(new StringMessageConverter());
        if (JACKSON_PRESENT) {
            messageConverters.add(new MappingJackson2MessageConverter());
        }
        if (FASTJSON_PRESENT) {
            try {
                messageConverters.add(
                    (MessageConverter)ClassUtils.forName(
                        "com.alibaba.fastjson.support.spring.messaging.MappingFastJsonMessageConverter",
                        ClassUtils.getDefaultClassLoader()).newInstance());
            } catch (ClassNotFoundException | IllegalAccessException | InstantiationException ignored) {
                //ignore this exception
            }
        }
        messageConverter = new CompositeMessageConverter(messageConverters);
    }

从源码来看支持四种convert分别是字节、String、JackSon、fastJson。

org.springframework.messaging.converter.AbstractMessageConverter#toMessage(java.lang.Object, org.springframework.messaging.MessageHeaders, java.lang.Object)

public final Message<?> toMessage(Object payload, @Nullable MessageHeaders headers, @Nullable Object conversionHint) {
		if (!canConvertTo(payload, headers)) {
			return null;
		}

		Object payloadToUse = convertToInternal(payload, headers, conversionHint);
		if (payloadToUse == null) {
			return null;
		}

		MimeType mimeType = getDefaultContentType(payloadToUse);
		if (headers != null) {
			MessageHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(headers, MessageHeaderAccessor.class);
			if (accessor != null && accessor.isMutable()) {
				if (mimeType != null) {
					accessor.setHeaderIfAbsent(MessageHeaders.CONTENT_TYPE, mimeType);
				}
				return MessageBuilder.createMessage(payloadToUse, accessor.getMessageHeaders());
			}
		}

		MessageBuilder<?> builder = MessageBuilder.withPayload(payloadToUse);
		if (headers != null) {
			builder.copyHeaders(headers);
		}
		if (mimeType != null) {
			builder.setHeaderIfAbsent(MessageHeaders.CONTENT_TYPE, mimeType);
		}
		return builder.build();
	}

// 只有jackson和fastjson才能canConvertTo,由于jackson的顺序在前面,就优先走了jackson

猜测,rocketMQ的MappingJackson2MessageConverter的objectMapper与springboot默认的不是一个对象,而rocketMQ的没有注册JavaTimeModule。

重写rocketMQ的json配置试一下

@Configuration
public class RocketConfig
{
    @Bean
    public RocketMQMessageConverter createRocketMQMessageConverter() {
        RocketMQMessageConverter converter = new RocketMQMessageConverter();
        CompositeMessageConverter compositeMessageConverter = (CompositeMessageConverter) converter.getMessageConverter();
        List<MessageConverter> messageConverterList = compositeMessageConverter.getConverters();
        /*for (int i = 0; i < messageConverterList.size(); i++)
        {
            // rocketMQ SpringBoot jackson转换LocalDate报错 暂时使用fastJson
            if (messageConverterList.get(i) instanceof MappingJackson2MessageConverter) {
                messageConverterList.remove(messageConverterList.get(i));
                i = i - 1;
            }
        }*/
        for (MessageConverter messageConverter : messageConverterList)
        {
            if (messageConverter instanceof MappingJackson2MessageConverter) {
                MappingJackson2MessageConverter jackson2MessageConverter = (MappingJackson2MessageConverter) messageConverter;
                ObjectMapper objectMapper = jackson2MessageConverter.getObjectMapper();
                objectMapper.registerModules(new JavaTimeModule());
            }
        }
        return converter;
    }
}

启动重试,哈哈不报错了,暂时先这么玩吧

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值