详解spring mvc4使用及json 日期转换解决方案

又到搭新开发环境的时候,总是不免去网上搜下目前最新的框架。spring是web开发必用的框架,于是乎下载了目前最新的spring4.0.3,同时越来越不想用struts2,想试试spring mvc,也将spring-webmvc4.0.3下了下来,投入两天时间学习后,发现还是挺优雅的,特别是从3.0后,spring mvc使用注解方式配制,以及对rest风格的支持,真是完美致极。

下面将这两天研究到的问题做个总结,供参考。

1.request对象的获取

方式1:在controller方法上加入request参数,spring会自动注入,如:

public String list(HttpServletRequest request,HttpServletResponse response)

方式2:在controller类中加入@Resource private HttpServletRequest request 属性,spring会自动注入,这样不知道会不会出现线程问题,因为一个controller实例会为多个请求服务,暂未测试。

方式3:在controller方法中直接写代码获取

HttpServletRequest request =((ServletRequestAttributes)
RequestContextHolder.getRequestAttributes()).getRequest();

方式4:在controller中加入以下方法,此方法会在执行此controller的处理方法之前执行

@ModelAttribute
private void initServlet(HttpServletRequest request,HttpServletResponse response) {
 //String p=request.getParameter("p");
 //this.req=request;//实例变量,有线程安全问题,可以使用ThreadLocal模式保存
}

2.response对象的获取

可以参照以上request的获取方式1和方式4,方式2和方式3对response对象无效!

3.表单提交之数据填充
直接在方法上加入实体对象参数,spring会自动填充对象中的属性,对象属性名要与<input>的name一致才会填充,如:

public boolean doAdd(Demo demo)

4.表单提交之数据转换-Date类型

在实体类的属性或get方法上加入 @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss"),那么表单中的日期字符串就会正确的转换为Date类型了。还有@NumberFormat注解,暂时没用,就不介绍了,一看就知道是对数字转换用的。

5.json数据返回

在方法上加入@ResponseBody,同时方法返回值为实体对象,spring会自动将对象转换为json格式,并返回到客户端。如下所示:

@RequestMapping("/json1")
@ResponseBody
public Demo json1() {
 Demo demo=new Demo();
 demo.setBirthday(new Date());
 demo.setCreateTime(new Date());
 demo.setHeight(170);
 demo.setName("tomcat");
 demo.setRemark("json测试");
 demo.setStatus((short)1);
 return demo;
}

注意:spring配置文件要加上:<mvc:annotation-driven/>,同时还要引入jackson-core.jar,jackson-databind.jar,jackson-annotations.jar(2.x的包)才会自动转换json

这种方式是spring提供的,我们还可以自定义输出json,以上第二条不是说了获取response对象吗,拿到response对象后,任由开发人员宰割,想怎么返回就怎么返回。

方法不要有返回值,如下:

@RequestMapping("/json2")
public void json2() {
 Demo demo=new Demo();
 demo.setBirthday(new Date());
 demo.setCreateTime(new Date());
 demo.setHeight(170);
 demo.setName("tomcat");
 demo.setRemark("json测试");
 demo.setStatus((short)1);
 String json=JsonUtil.toJson(obj);//;json处理工具类
 HttpServletResponse response = //获取response对象
 response.getWriter().print(json);
}

OK,一切很完美。接着恶心的问题迎面而来,date类型转换为json字符串时,返回的是long time值,如果你想返回“yyyy-MM-dd HH:mm:ss”格式的字符串,又要自定义了。我很奇怪,不是有@DateTimeFormat注解吗,为什么不利用它。难道@DateTimeFormat只在表单提交时,将字符串转换为date类型,而date类型转换为json字符串时,就不用了。带着疑惑查源码,原来spring使用jackson转换json字符,而@DateTimeFormat是spring-context包中的类,jackson如何转换,spring不方便作过多干涉,于是只能遵守jackson的转换规则,自定义日期转换器。

先写一个日期转换器,如下:

public class JsonDateSerializer extends JsonSerializer<Date> {
 private SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 @Override
 public void serialize(Date date, JsonGenerator gen, SerializerProvider provider)
   throws IOException, JsonProcessingException {
  String value = dateFormat.format(date);
  gen.writeString(value);
 }
}

在实体类的get方法上配置使用转换器,如下:

@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@JsonSerialize(using=JsonDateSerializer.class)
public Date getCreateTime() {
 return this.createTime;
}

OK,到此搞定。

你真的满意了吗,这么不优雅的解决方案,假设birthday属性是这样的,只有年月日,无时分秒

@DateTimeFormat(pattern="yyyy-MM-dd")
public Date getBirthday() {
 return this.birthday;
} 

这意味着,又要为它定制一个JsonDate2Serializer的转换器,然后配置上,像这样

@DateTimeFormat(pattern="yyyy-MM-dd")
@JsonSerialize(using=JsonDate2Serializer.class)
public Date getBirthday() {
 return this.birthday;
} 

假设还有其它格式的Date字段,还得要为它定制另一个转换器。my god,请饶恕我的罪过,不要让我那么难受

经过分析源码,找到一个不错的方案,此方案将不再使用@JsonSerialize,而只利用@DateTimeFormat配置日期格式,jackson就可以正确转换,但@DateTimeFormat只能配置在get方法上,这也没什么关系。

先引入以下类,此类对jackson的ObjectMapper类做了注解扫描拦截,使它也能对加了@DateTimeFormat的get方法应用日期格式化规则

package com.xxx.utils;
import java.io.IOException;
import java.lang.reflect.AnnotatedElement;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; 

/**
 * json处理工具类
 * @author zhangle
 */
@Component
public class JsonUtil { 

  private static final String DEFAULT_DATE_FORMAT="yyyy-MM-dd HH:mm:ss";
  private static final ObjectMapper mapper; 

  public ObjectMapper getMapper() {
    return mapper;
  } 

  static {
    SimpleDateFormat dateFormat = new SimpleDateFormat(DEFAULT_DATE_FORMAT); 

    mapper = new ObjectMapper();
    mapper.setDateFormat(dateFormat);
    mapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {
      @Override
      public Object findSerializer(Annotated a) {
        if(a instanceof AnnotatedMethod) {
          AnnotatedElement m=a.getAnnotated();
          DateTimeFormat an=m.getAnnotation(DateTimeFormat.class);
          if(an!=null) {
            if(!DEFAULT_DATE_FORMAT.equals(an.pattern())) {
              return new JsonDateSerializer(an.pattern());
            }
          }
        }
        return super.findSerializer(a);
      }
    });
  } 

  public static String toJson(Object obj) {
    try {
      return mapper.writeValueAsString(obj);
    } catch (Exception e) {
      throw new RuntimeException("转换json字符失败!");
    }
  } 

  public <T> T toObject(String json,Class<T> clazz) {
    try {
      return mapper.readValue(json, clazz);
    } catch (IOException e) {
      throw new RuntimeException("将json字符转换为对象时失败!");
    }
  } 

  public static class JsonDateSerializer extends JsonSerializer<Date>{
   private SimpleDateFormat dateFormat;
   public JsonDateSerializer(String format) {
     dateFormat = new SimpleDateFormat(format);
    } 

   @Override
   public void serialize(Date date, JsonGenerator gen, SerializerProvider provider)
     throws IOException, JsonProcessingException {
    String value = dateFormat.format(date);
    gen.writeString(value);
   }
  }
}

再将<mvc:annotation-driven/>改为以下配置,配置一个新的json转换器,将它的ObjectMapper对象设置为JsonUtil中的objectMapper对象,此转换器比spring内置的json转换器优先级更高,所以与json有关的转换,spring会优先使用它。

<mvc:annotation-driven>
 <mvc:message-converters>
  <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
   <property name="objectMapper" value="#{jsonUtil.mapper}"/>
   <property name="supportedMediaTypes">
    <list>
     <value>text/json;charset=UTF-8</value>
    </list>
   </property>
  </bean>
 </mvc:message-converters>
</mvc:annotation-driven>

接下来就可以这样配置实体类,jackson也能正确转换Date类型

@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
public Date getCreateTime() {
 return this.createTime;
}
@DateTimeFormat(pattern="yyyy-MM-dd")
public Date getBirthday() {
 return this.birthday;
} 

完毕,一切都完美了。

补充

写了那么多,发现白忙活了一场,原来jackson也有一个@JsonFormat注解,将它配置到Date类型的get方法上后,jackson就会按照配置的格式转换日期类型,而不自定义转换器类,欲哭无泪啊。辛苦了那么多,其实别人早已提供,只是没有发现而已。

不说了,直接上方案吧。

1.spring配置照样是这样:

<mvc:annotation-driven>

2.JsonUtil可以不用了,但如果要自己从response对象输出json,那么还是可以用,但改成了这样

package com.xxx.utils; 

import java.io.IOException;
import java.text.SimpleDateFormat;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.databind.ObjectMapper; 

/**
 * json处理工具类
 * @author zhangle
 */
@Component
public class JsonUtil { 

 private static final String DEFAULT_DATE_FORMAT="yyyy-MM-dd HH:mm:ss";
 private static final ObjectMapper mapper; 

 static {
  SimpleDateFormat dateFormat = new SimpleDateFormat(DEFAULT_DATE_FORMAT);
  mapper = new ObjectMapper();
  mapper.setDateFormat(dateFormat);
 } 

 public static String toJson(Object obj) {
  try {
   return mapper.writeValueAsString(obj);
  } catch (Exception e) {
   throw new RuntimeException("转换json字符失败!");
  }
 } 

 public <t> T toObject(String json,Class<t> clazz) {
  try {
   return mapper.readValue(json, clazz);
  } catch (IOException e) {
   throw new RuntimeException("将json字符转换为对象时失败!");
  }
 }
}

3.实体类的get方法就需要多一个@JsonFormat的注解配置

@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
public Date getCreateTime() {
return this.createTime;
}
@DateTimeFormat(pattern="yyyy-MM-dd")
@JsonFormat(pattern="yyyy-MM-dd",timezone = "GMT+8")
public Date getBirthday() {
 return this.birthday;
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • spring mvc4的日期/数字格式化、枚举转换示例

    日期.数字格式化显示,是web开发中的常见需求,spring mvc采用XXXFormatter来处理,先看一个最基本的单元测试: package com.cnblogs.yjmyzz.test; import java.math.BigDecimal; import java.util.Date; import java.util.Locale; import org.junit.Test; import org.springframework.context.i18n.LocaleConte

  • Spring MVC自定义日期类型转换器实例详解

    Spring MVC自定义日期类型转换器实例详解 WEB层采用Spring MVC框架,将查询到的数据传递给APP端或客户端,这没啥,但是坑的是实体类中有日期类型的属性,但是你必须提前格式化好之后返回给它们.说真的,以前真没这样做过,之前都是一口气查询到数据,然后在jsp页面上格式化,最后展示给用户.但是这次不同,这次我纯属操作数据,没有页面.直接从数据库拿数据给它们返数据.它们给我传数据我持久化数据,说到这里一个小问题就默默的来了. 首先把问题还原一下吧(这是一个数据导出功能),下图中用红框圈

  • springmvc学习笔记-返回json的日期格式问题的解决方法

    springmvc学习笔记--json--返回json的日期格式问题 (一)输出json数据  springmvc中使用jackson-mapper-asl即可进行json输出,在配置上有几点: 1.使用mvc:annotation-driven 2.在依赖管理中添加jackson-mapper-asl <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-mappe

  • SpringMVC用JsonSerialize日期转换方法

    最近在用SpringMvc做Http接口时,对方在调用我接口时发现Date格式的默认转化为long,因此在前端页面看到的是一串数字. 我们可以自定义代码的转换器,返回数据到前台的时候就可以按照我们的需要返回格式化后的字符串类型数据. package com.cnpc.mall.web.utils; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import org.cod

  • SpringMVC对日期类型的转换示例

    在做web开发的时候,页面传入的都是String类型,SpringMVC可以对一些基本的类型进行转换,但是对于日期类的转换可能就需要我们配置. 1.如果查询类使我们自己写,那么在属性前面加上@DateTimeFormat(pattern = "yyyy-MM-dd")  ,即可将String转换为Date类型,如下 @DateTimeFormat(pattern = "yyyy-MM-dd") private Date createTime; 2.如果我们只负责we

  • SpringMVC中日期格式的转换

    解决日期提交转换异常的问题 由于日期数据有很多种格式,所以springmvc没办法把字符串转换成日期类型.所以需要自定义参数绑定.前端控制器接收到请求后,找到注解形式的处理器适配器,对RequestMapping标记的方法进行适配,并对方法中的形参进行参数绑定.在springmvc这可以在处理器适配器上自定义Converter进行参数绑定.如果使用<mvc:annotation-driven/>可以在此标签上进行扩展. 1.自定义DataConvertor类, 并实现Convertor接口 p

  • 详解spring mvc4使用及json 日期转换解决方案

    又到搭新开发环境的时候,总是不免去网上搜下目前最新的框架.spring是web开发必用的框架,于是乎下载了目前最新的spring4.0.3,同时越来越不想用struts2,想试试spring mvc,也将spring-webmvc4.0.3下了下来,投入两天时间学习后,发现还是挺优雅的,特别是从3.0后,spring mvc使用注解方式配制,以及对rest风格的支持,真是完美致极. 下面将这两天研究到的问题做个总结,供参考. 1.request对象的获取 方式1:在controller方法上加入

  • 详解Spring Security如何配置JSON登录

    spring security用了也有一段时间了,弄过异步和多数据源登录,也看过一点源码,最近弄rest,然后顺便搭oauth2,前端用json来登录,没想到spring security默认居然不能获取request中的json数据,谷歌一波后只在stackoverflow找到一个回答比较靠谱,还是得要重写filter,于是在这里填一波坑. 准备工作 基本的spring security配置就不说了,网上一堆例子,只要弄到普通的表单登录和自定义UserDetailsService就可以.因为需

  • 详解Spring MVC4 纯注解配置教程

    阅读本文需要又一定的sping基础,最起码要成功的运行过一个SpringMvc项目. 在传统的Spring项目中,我们要写一堆的XML文件.而这些XML文件格式要求又很严格,很不便于开发.而网上所谓的0配置,并不是纯粹的0配置,还是要写一些xml配置,只是用了几个@Service,@Controller注解而已. 在这里,我讲介绍一种新的配置方式,一行XML代码都不需要,什么web.xml,Application-context.xml,Beans.xml,统统去死吧! 首先建立一个Maven项

  • 详解Spring data 定义默认时间与日期的实例

    详解Spring data 定义默认时间与日期的实例 前言: 需求是这样的: 1. 创建时间与更新时间只能由数据库产生,不允许在实体类中产生,因为每个节点的时间/时区不一定一直.另外防止人为插入自定义时间时间. 2. 插入记录的时候创建默认时间,创建时间不能为空,时间一旦插入不允许日后在实体类中修改. 3. 记录创建后更新日志字段为默认为 null 表示该记录没有被修改过.一旦数据被修改,修改日期字段将记录下最后的修改时间. 4. 甚至你可以通过触发器实现一个history 表,用来记录数据的历

  • 详解spring cloud config实现datasource的热部署

    关于spring cloud config的基本使用,前面的博客中已经说过了,如果不了解的话,请先看以前的博客 spring cloud config整合gitlab搭建分布式的配置中心 spring cloud config分布式配置中心的高可用 今天,我们的重点是如何实现数据源的热部署. 1.在客户端配置数据源 @RefreshScope @Configuration// 配置数据源 public class DataSourceConfigure { @Bean @RefreshScope

  • 详解spring boot rest例子

    简介:本文将帮助您使用 Spring Boot 创建简单的 REST 服务. 你将学习 什么是 REST 服务? 如何使用 Spring Initializr 引导创建 Rest 服务应用程序? 如何创建获取 REST 服务以检索学生注册的课程? 如何为学生注册课程创建 Post REST 服务? 如何利用 postman 执行 rest 服务? 本教程使用的 rest 服务 在本教程中,我们将使用适当的 URI 和 HTTP 方法创建三个服务: @GetMapping("/ students

  • 详解Spring集成Redis的两种方式

    目录 一.使用Jedis方式集成 1.增加依赖 2.配置项 3.配置连接池 4.测试 使用spring-data-redis 1.引入依赖 2.配置项 3.使用 4.可能会遇到的坑 哨兵和集群 总结: 在工作中,我们用到分布式缓存的时候,第一选择就是Redis,今天介绍一下SpringBoot如何集成Redis的,分别使用Jedis和Spring-data-redis两种方式. 一.使用Jedis方式集成 1.增加依赖 <!-- spring-boot-starter-web不是必须的,这里是为

  • 详解Spring中@Valid和@Validated注解用法

    目录 案例引入 @Valid 详解 @Validated 详解 @Valid 和 @Validated 比较 案例引入 下面我们以新增一个员工为功能切入点,以常规写法为背景,慢慢烘托出 @Valid 和 @Validated 注解用法详解. 那么,首先,我们会有一个员工对象 Employee,如下 : /** * 员工对象 * * @author sunnyzyq * @since 2019/12/13 */ public class Employee { /** 姓名 */ public St

  • 详解Spring的核心机制依赖注入

    详解Spring的核心机制依赖注入 对于一般的Java项目,他们都或多或少有一种依赖型的关系,也就是由一些互相协作的对象构成的.Spring把这种互相协作的关系称为依赖关系.如A组件调用B组件的方法,可称A组件依赖于B组件,依赖注入让Spring的Bean以配置文件组织在一起,而不是以硬编码的方式耦合在一起 一.理解依赖注入 依赖注入(Dependency Injection) = 控制反转(Inversion ofControl,IoC):当某个Java实例(调用者)需另一个Java实例(被调

  • 详解 Spring注解的(List&Map)特殊注入功能

    详解 Spring注解的(List&Map)特殊注入功能 最近接手一个新项目,已经没有原开发人员维护了.项目框架是基于spring boot进行开发.其中有两处Spring的注解花费了大量的时间才弄明白到底是怎么用的,这也涉及到spring注解的一个特殊的注入功能. 首先,看到代码中有直接注入一个List和一个Map的.示例代码如下: @Autowired private List<DemoService> demoServices; @Autowired private Map<

随机推荐