Spring项目前端显示的时间差8小时分析

  在开发中,有时听到其它项目的同事说前端展示的时间差了8小时,是由于时区的问题,因为中国恰好是GMT+8,比格林尼治时间早了8小时。但奇怪的是,为什么我们项目就没有碰到过呢?于是,特别写了一个简单的例子,确认了一下我们的确实没有问题,并咨询了其它项目组同事是怎么出现的,特在此记录一下。

一.写一个简单的demo,还原各种场景

        定义Controller

@RequestMapping("/list")
public String showAll(ModelMap modelMap) {

	List<Article> articles = mapper.getArticlesByUserId(1);
	modelMap.addAttribute("articles", articles);
	return "main.ftl";
}

@RequestMapping("/list2")
public ModelAndView listall(HttpServletRequest request, HttpServletResponse response) {

	List<Article> articles = mapper.getArticlesByUserId(1);
	ModelAndView mav = new ModelAndView("list");
	mav.addObject("articles", articles);
	return mav;
}

@ResponseBody
@RequestMapping("/list3")
public Map<String, Object> listall3(HttpServletRequest request, HttpServletResponse response) {

	List<Article> articles = mapper.getArticlesByUserId(1);
	Map<String, Object> map = new HashMap<String, Object>();
	map.put("articles", articles);
	return map;
}

1./list通过freemark模板展示,时间显示OK

<#list articles as article>
	<div>${article.id}. ${article.title}: ${article.content}: ${(article.updateTime)?string("yyyy-MM-dd HH:mm:ss")}</div>
</#list>

        访问请求,没有时间问题,如下所示:



2./list2通过jsp显示,时间也OK

list.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>article list</title>
</head>
<body>
	<c:forEach items="${articles}" var="item">  
        ${item.id }--${item.title }--${item.content}--${item.updateTime}<br />
	</c:forEach>
</body>
</html>

        访问请求,没有时间问题,如下所示:


 

3./list3直接返回map,json串展示


        时间字段变成了长整型显示,但时间还是对的,在我们项目实际应用中,前端JS转换成字符串展示出来。

        原来其它出问题的项目,他们是在后台的时间字段上通过@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")方式转换成字符串,然后直接在前端展示的。

        于是,我把Article.java的updateTime字段上加上@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")注解,再次请求页面显示如下,updateTime比数据库中的真正时间晚了8小时:


        在《SpringMVC中使用@RequestBody,@ResponseBody注解实现Java对象和XML/JSON数据自动转换(下)》一文中,我们知道,转换为JSON格式的对象提供前端显示,这个过程被Spring MVC的@RestController 注解识别并使用MappingJackson2HttpMessageConverter 进行消息转换,而我们的工程中都有jackson,且Jackson在对时间类型字段序列化(java.util.Date类型format为字符串)时是以GMT+0进行的,而不是"本地化"的时区设置。Jackson FAQ: Date Handling:All time objects that have associated TimeZone (java.util.Calendar etc) that Jackson constructs use the standard timezone (GMT), not the local time zone (whatever that might be). That is: Jackson defaults to using GMT for all processing unless specifically told otherwise.

        查资料发现,这个问题可以通过设置主机默认时区来解决,如下所示:


        但,通过查看源码,publicvoidconfigure(ObjectMapper objectMapper)方法里面设置的timezone恰好是没有赋值的。

二.解决办法

1.直接返回Date类型,让前端js或jsp来处理

2.Spring MVC的配置文件中的标签<mvc:annotation-driven>会默认实例化各种消息转换对象实例,解决上述问题的关键就是用自定的带时区的JSON消息转换器替换默认的。

<mvc:annotation-driven></mvc:annotation-driven>

修改为:

<mvc:annotation-driven>
	<mvc:message-converters>
	<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
		<property name="objectMapper">
				<bean class="com.fasterxml.jackson.databind.ObjectMapper">
					<property name="timeZone">
					<bean class="java.util.TimeZone" factory-method="getDefault" />
					</property>
				</bean>
		</property>
	</bean>
	</mvc:message-converters>
</mvc:annotation-driven>

        实例代码见附件。 

参考文章:https://blog.csdn.net/oShuSheng1/article/details/49614867

https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc#mvc-config-message-converters

猜你喜欢

转载自bijian1013.iteye.com/blog/2419741