SpringMVC使用Jackson返回JSON时日期少一天的问题

1、原来为解决IE中出现下载JSON的问题,手动配置了HttpMessageConverter
<mvc:annotation-driven validator="validator" conversion-service="conversionService" content-negotiation-manager="contentNegotiationManager" enableMatrixVariables="true">
	<mvc:message-converters register-defaults="true">  
		<bean class="org.springframework.http.converter.StringHttpMessageConverter"> 
			<constructor-arg ref="encoding"/>     
			<property name="supportedMediaTypes">
				<list>
					<bean class="org.springframework.http.MediaType">
						<constructor-arg index="0" value="text"/>
						<constructor-arg index="1" value="plain"/>
						<constructor-arg index="2" ref="UTF-8"/>
					</bean>
					<bean class="org.springframework.http.MediaType">
						<constructor-arg index="0" value="*"/>
						<constructor-arg index="1" value="*"/>
						<constructor-arg index="2" ref="UTF-8"/>
					</bean>
				</list>
			</property>   
		</bean>
        <ref bean="jacksonMessageConverter"/>
	</mvc:message-converters>  
</mvc:annotation-driven>
<!-- JSON转换器,避免IE出现下载JSON文件的情况 -->
<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
	<property name="supportedMediaTypes">
		<list>
			<value>text/html;charset=UTF-8</value>
		</list>
	</property>
</bean>

2、使用时发现解析Date日期都是Long类型,想解析为字符串,查看文档,在Date字段上加@JsonFormat

class Demo
{
	private String name;
	private Integer age;
	@JsonFormat(pattern = "yyyy-MM-dd")
	private Date birthday;
}  
但是查看结果后,发现日期少一天。查看源码发现要加上时区@JsonFormat(pattern = "yyyy-MM-dd",timezone="GMT+08")(东八区)。但是发现这样很麻烦,如果日期字段多的话就很费事,并且不想在字段上限定太死。

因为使用Jackson解析,就查看Jackson解析的代码,发现使用类ObjectMapper。查看源码在ObjectMapper中可以设置时区,就手动配置ObjectMapper并设置了时区

<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
	<property name="supportedMediaTypes">
		<list>
			<value>text/html;charset=UTF-8</value>
		</list>
	</property>
	<property name="objectMapper" ref="objectMapper" />
</bean>
<bean id="objectMapper" class="com.fasterxml.jackson.databind.ObjectMapper">
	<property name="timeZone">
		<bean class="java.util.TimeZone" factory-method="getTimeZone" >
			<constructor-arg value="GMT+08"/>
		</bean>
	</property>
</bean>
但是配置后发现还是少一天。头都大了,怎么办呢?

于是Debug看看流程,发现在正真解析时用的MappingJackson2HttpMessageConverter不是手动配置的MappingJackson2HttpMessageConverter实例,怎么回事呢?

查看源码,发现问题处在<mvc:message-converters register-defaults="true"> ,register-defaults是是否注册默认的转换器,SpringMVC默认注册了7种转换器,加上自定义的两个总共9个,自定义的在前面,默认的在后面(存放在List集合)。

这样的话将register-defaults设为false就可以解决了,测试后发现OK。

3、但是还有问题,照上面所说的在执行时默认应该是拿前面的啊,怎么会拿后面的?怎么回事?只能查看源码了。

于是Debug了下,看看SpingMVC的代码执行过程。找到SpingMVC在响应解析内容时,获取转换器在org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor的writeWithMessageConverters方法中,

	protected <T> void writeWithMessageConverters(T returnValue, MethodParameter returnType,
			ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
			throws IOException, HttpMediaTypeNotAcceptableException {

		Class<?> returnValueClass = returnValue.getClass();//返回值类型
		HttpServletRequest servletRequest = inputMessage.getServletRequest();
		//application/json, text/javascript, */*;q=0.01
		List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(servletRequest);//获取客户端浏览器支持的类型
		//[text/html;charset=UTF-8, application/json;charset=UTF-8, application/json;charset=UTF-8, application/*+json;charset=UTF-8]
		List<MediaType> producibleMediaTypes = getProducibleMediaTypes(servletRequest, returnValueClass);//根据返回值和请求获取可生产的类型

		Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>();//兼容的类型
		for (MediaType requestedType : requestedMediaTypes) {
			for (MediaType producibleType : producibleMediaTypes) {
				if (requestedType.isCompatibleWith(producibleType)) {
					compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType));//增加兼容类型
				}
			}
		}
		if (compatibleMediaTypes.isEmpty()) {
			throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);//没有可支持的类型,抛出异常
		}

		List<MediaType> mediaTypes = new ArrayList<MediaType>(compatibleMediaTypes);
		MediaType.sortBySpecificityAndQuality(mediaTypes);
		//[application/json;charset=UTF-8, text/html;charset=UTF-8;q=0.01, application/json;charset=UTF-8;q=0.01, application/*+json;charset=UTF-8;q=0.01]

		MediaType selectedMediaType = null;//选择的具体类型
		for (MediaType mediaType : mediaTypes) {
			if (mediaType.isConcrete()) {//是否是具体的类型
				selectedMediaType = mediaType;
				break;
			}
			else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {
				//是*/* 或 application,则设置为默认的类型 application/octet-stream
				selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
				break;
			}
		}

		if (selectedMediaType != null) {
			//application/json;charset=UTF-8
			selectedMediaType = selectedMediaType.removeQualityValue();//去除q=?
			for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
				if (messageConverter.canWrite(returnValueClass, selectedMediaType)) {//判断转换器是否支持选择的类型
					//获取到具体的转换器,向客户端发送信息(转换后的信息),具体的转换工作有转换器完成
					((HttpMessageConverter<T>) messageConverter).write(returnValue, selectedMediaType, outputMessage);
					if (logger.isDebugEnabled()) {
						logger.debug("Written [" + returnValue + "] as \"" + selectedMediaType + "\" using [" +
								messageConverter + "]");
					}
					return;
				}
			}
		}
		throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
	}

发现预先支持的是application/json;charset=UTF-8,而我自己配置的不支持application/json类型,于是改为

<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
	<property name="supportedMediaTypes">
		<list>
			<value>text/html;charset=UTF-8</value>
			<value>application/json;charset=UTF-8</value>
		</list>
	</property>
	<property name="objectMapper" ref="objectMapper" />
</bean>

就OK了。问题解决,也不用设置时区了。

4、当然还可以给ObjectMapper设置DateFormat,全局统一设置日期转换格式。需要个性化设置的地方在设置@JsonFormat就可以

<bean id="objectMapper" class="com.fasterxml.jackson.databind.ObjectMapper">
	<property name="timeZone">
		<bean class="java.util.TimeZone" factory-method="getTimeZone" >
			<constructor-arg value="GMT+08"/>
		</bean>
	</property>
	<property name="dateFormat">
		<bean class="java.text.SimpleDateFormat">
			<constructor-arg type="java.lang.String" value="yyyy-MM-dd"/>
		</bean>
	</property>
</bean>

最终配置

<mvc:annotation-driven validator="validator" conversion-service="conversionService" content-negotiation-manager="contentNegotiationManager" enableMatrixVariables="true">
	<mvc:message-converters register-defaults="true">  
		<bean class="org.springframework.http.converter.StringHttpMessageConverter"> 
			<constructor-arg ref="encoding"/>     
			<property name="supportedMediaTypes">
				<list>
					<bean class="org.springframework.http.MediaType">
						<constructor-arg index="0" value="text"/>
						<constructor-arg index="1" value="plain"/>
						<constructor-arg index="2" ref="UTF-8"/>
					</bean>
					<bean class="org.springframework.http.MediaType">
						<constructor-arg index="0" value="*"/>
						<constructor-arg index="1" value="*"/>
						<constructor-arg index="2" ref="UTF-8"/>
					</bean>
				</list>
			</property>   
		</bean>
        <span style="white-space:pre">	</span><ref bean="jacksonMessageConverter"/>
	</mvc:message-converters>  
</mvc:annotation-driven>
<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
	<property name="supportedMediaTypes">
		<list>
			<value>text/html;charset=UTF-8</value>
			<value>application/json;charset=UTF-8</value>
		</list>
	</property>
	<property name="objectMapper" ref="objectMapper" />
</bean>
<bean id="objectMapper" class="com.fasterxml.jackson.databind.ObjectMapper">
	<property name="timeZone">
		<bean class="java.util.TimeZone" factory-method="getTimeZone" >
			<constructor-arg value="GMT+08"/>
		</bean>
	</property>
	<property name="dateFormat">
		<bean class="java.text.SimpleDateFormat">
			<constructor-arg type="java.lang.String" value="yyyy-MM-dd"/>
		</bean>
	</property>
</bean>



解决了SpringMVC响应JSON的日期Date类型的问题,又进一步了解了SpringMVC的工作流程。








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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值