spring mvc3 + fastjson

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: spring 3可以支持Rest风格参数,其内置了jackson框架作为REST的json参数转换成javabean对象,以及bean对象转换成json参数。

spring 3可以支持Rest风格参数,其内置了jackson框架作为REST的json参数转换成javabean对象,以及bean对象转换成json参数。

下文以spring 3.1.1 + fastjson为例(低于这个版本的不知道能不能行),说明如何使用springmvc构造resuful参数及输出。

不说废话了,直接上代码:Controller类:


@Controller
public class TestCon
{
    @ResponseBody
    @RequestMapping("/test")
    public Object test(@RequestBody
    final TBean tBean)
    {
        System.out.println(tBean);
        final List<Object> lists = new ArrayList<Object>();
        lists.add("3434");
        lists.add(tBean);
        lists.add(new String[] { "a", "b" });

        return lists;
    }
}

其中:@RequestBody可以使请求中的REST风格的json字符串直接转换成我们想要的参数TBean(当然,低层是使用fastjson等框架转换).

此方法的返回值为一个List对象,经过转换成json输出后,是一个js数组格式的内容。

@RequestBody不可少,表示将方法的返回值作为响应内容。

数据bean对象。


public class TBean
{

    private String id;

    //....
}


好了,最关键的一步:在springmvc 的配置文件中(XXX-servlet.xml)配置json格式转换方式:

<!-- 方式一 -->
<!--

     <mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter" />
        </mvc:message-converters>
    </mvc:annotation-driven>
 -->
 
<!-- 方式二 -->
 
    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <property name="messageConverters">
            <list>
                <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter"></bean>
            </list>
        </property>
    </bean>

需要注意的是:方式一和方式二,选择一种实现即可。其它方式一是使用了命名空间的引入,需要引入mvc的命名空间,sprng3.0 不支持这种写法。这两种写法不能同时存在,如果同时存在,功能将异常。

核心代码就是这样的。当然不可缺少的要把fastjson框架的jar包引入到工程中。

模拟发起请求:

其中:{id:'34'}为REST风格的字符串,将被fastjson转换成TBean对象,直接扔给控制器的方法调用。

Content-Type: application/json; charset=utf-8
Accept: application/json
为请求头信息,以上的代码只支持Context-Type为application/json格式的头,如果没有此头或头内容不是application/json,spring将抛出415的http返回码,导致失败:

如果配置了log4j打印spring debug级别的日志,

可以看到日志中出现如下信息:

************************************************************未设置Content-Type信息的错误日志********************************************************************************

DEBUG DefaultAnnotationHandlerMapping - Mapping [/test.do] to HandlerExecutionChain with handler [test.controllers.TestCon@c81672] and 1 interceptor
DEBUG AnnotationMethodHandlerExceptionResolver - Resolving exception from handler [test.controllers.TestCon@c81672]: org.springframework.web.HttpMediaTypeNotSupportedException: Cannot extract parameter (TBean tBean): no Content-Type found
DEBUG ResponseStatusExceptionResolver - Resolving exception from handler [test.controllers.TestCon@c81672]: org.springframework.web.HttpMediaTypeNotSupportedException: Cannot extract parameter (TBean tBean): no Content-Type found
DEBUG DefaultHandlerExceptionResolver - Resolving exception from handler [test.controllers.TestCon@c81672]: org.springframework.web.HttpMediaTypeNotSupportedException: Cannot extract parameter (TBean tBean): no Content-Type found
DEBUG DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'dispatcher': assuming HandlerAdapter completed request handling

************************************************************设置为非application/json的错误日志*********************************************************************************

DEBUG DefaultAnnotationHandlerMapping - Mapping [/test.do] to HandlerExecutionChain with handler [test.controllers.TestCon@c81672] and 1 interceptor
DEBUG AnnotationMethodHandlerExceptionResolver - Resolving exception from handler [test.controllers.TestCon@c81672]: org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/pan' not supported
DEBUG ResponseStatusExceptionResolver - Resolving exception from handler [test.controllers.TestCon@c81672]: org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/pan' not supported
DEBUG DefaultHandlerExceptionResolver - Resolving exception from handler [test.controllers.TestCon@c81672]: org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/pan' not supported
DEBUG DispatcherServlet - Null ModelAndView returned to DispatcherServlet with name 'dispatcher': assuming HandlerAdapter completed request handling

如果content-type输入正确的话,将会看到正确的输出结果:

*******************************************分隔线*****************************************************************************************************************************************

如果你不需要要求输入Context-Type即可解析的话,还有另一种方式实现,参考:

(以下内容转自:http://zjumty.iteye.com/blog/1927890

自定义Spring MVC3的参数映射和返回值映射 + fastjson
首先说一下场景:在一些富客户端Web应用程序中我们会有比较多的Ajax调用,并且希望与服务器交互的数据需要是复杂的JSON对象。 fastjon是一个非常高效的JSON序列化和反序列化库,我希望我们输入的JSON串能通过fastjson直接反序列化为一个复杂的JavaBean对象,同时我的返回值能够能通过fastjson序列化为JSON串。
所谓复杂的JavaBean就是,不仅仅只有一层属性,而是属性也是JavaBean的情况, 例如:


Java代码 收藏代码
public class FooBean {
private String name;
private Long id;
private Date birthday;
private List<Address> addresses;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public List<Address> getAddresses() {
return addresses;
}
public void setAddresses(List<Address> addresses) {
this.addresses = addresses;
}
@Override
public String toString() {
return "FooBean{" +
"name='" + name + '\'' +
", id=" + id +
", birthday=" + birthday +
", addresses=" + addresses +
'}';
}
}



Java代码 收藏代码
public class Address {
private String street;
private int number;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
@Override
public String toString() {
return "Address{" +
"street='" + street + '\'' +
", number=" + number +
'}';
}
}


当然,结构还可以再复杂,Adress对象里还可以又复杂JavaBean的属性。
在SpringMVC3中我们可以把输入简单的映射为某个Action方法的参数, 例如:


Java代码 收藏代码
@RequestMapping(value="/someAction", method=RequestMethod.POST)
public String processSubmit(FooBean fooBean, Model model) {
// 利用fooBean
return “views/some_page”;
}


用Spring MVC3, 我们可以把Form里的字段轻松的映射到JavaBean的属性。 Spring MVC3 提供了丰富的参数映射机制, 详细信息可以参见这里
同时对于Spring MVC3默认的提供的映射机制不能涵盖的对象,我们可以通过扩展HandlerMethodArgumentResolver和HttpMessageConverter的机制来实现。
HandlerMethodArgumentResolver对应输入, HttpMessageConverter对应输出
假设对于上面的FooBean, 我们有这样一个JSON对象和它对应:


Javascript代码 收藏代码
var data = {
name : "matianyi",
id : 12345,
birthday : "1983-07-01 01:12:12",
addresses : [
{
street : "street1",
number : 1
},
{
street : "street2",
number : 2
}
]
};


Spring MVC3 本身也提供直接把JSON对象映射到JavaBean的功能,例如MappingJackson2HttpMessageConverter和MappingJackson2JsonView。
在这里我们希望通过fastjson来实现序列化和反序列化。所以我们要自定义一个HandlerMethodArgumentResolver用来指定HttpServletRequest的Body映射到一个JavaBean。并且返回的JavaBean通过fastjson序列化。
方法的定义是这样的:


Java代码 收藏代码
@RequestMapping(value = "/fastjson", method = RequestMethod.POST)
public @ResponseBody FooBean fastjson2(@FastJson FooBean foo) {
System.out.println(foo);
return foo;
}


首先这里有个@FastJson的标注,这是我们为了让自己的HandlerMethodArgumentResolver能够识别这个参数是需要自己来处理而定义的一个Annotation


Java代码 收藏代码
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FastJson {
}


然后就是定义一个FastJsonArgumentResolver,来对HttpServletRequest的body进行解析:


Java代码 收藏代码
public class FastJsonArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterAnnotation(FastJson.class) != null;
}
@Override

public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
// content-type不是json的不处理
if (!request.getContentType().contains("application/json")) {
return null;
}
// 把reqeust的body读取到StringBuilder
BufferedReader reader = request.getReader();
StringBuilder sb = new StringBuilder();
char[] buf = new char[1024];
int rd;
while((rd = reader.read(buf)) != -1){
sb.append(buf, 0, rd);
}
// 利用fastjson转换为对应的类型
if(JSONObjectWrapper.class.isAssignableFrom(parameter.getParameterType())){
return new JSONObjectWrapper(JSON.parseObject(sb.toString()));
} else {
return JSON.parseObject(sb.toString(), parameter.getParameterType());
}
}
}


在这里,我们只针对content-type是application/json的对象做处理,最后通过JSON.parseObject方法简单的把JSON串反序列化为指定的类型。
这里有一个JSONObjectWrapper对象需要解释一下。 原本我是想如果Action方法的参数的类型是JSONObject这样的原始类型的话就直接利用JSON.parseObject(sb.toString())映射过去。 但是由于JSONObject实现了Map结果,所以Spring MVC3的默认处理器MapMethodProcessor会先起作用,这样就不能正常的映射成JSONObject对象了。 没有办法做了一个简单的JSONObject包装类,以使MapMethodProcessor不能对其进行处理。


Java代码 收藏代码
public class JSONObjectWrapper {
private JSONObject jsonObject;
public JSONObjectWrapper(JSONObject jsonObject) {
this.jsonObject = jsonObject;
}
public JSONObject getJSONObject() {
return jsonObject;
}
}


这里顺便提一下,Spring MVC自己的HandlerMethodArgumentResolver有哪些,并且会以什么样的顺序执行呢?
其实定义在RequestMappingHandlerAdapter里:


Java代码 收藏代码
/**
* Return the list of argument resolvers to use including built-in resolvers
* and custom resolvers provided via {@link #setCustomArgumentResolvers}.
*/
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
// Annotation-based argument resolution
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
resolvers.add(new RequestParamMapMethodArgumentResolver());
resolvers.add(new PathVariableMethodArgumentResolver());
resolvers.add(new PathVariableMapMethodArgumentResolver());
resolvers.add(new MatrixVariableMethodArgumentResolver());
resolvers.add(new MatrixVariableMapMethodArgumentResolver());
resolvers.add(new ServletModelAttributeMethodProcessor(false));
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters()));
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters()));
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
// Type-based argument resolution
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters()));
resolvers.add(new RedirectAttributesMethodArgumentResolver());
resolvers.add(new ModelMethodProcessor());
resolvers.add(new MapMethodProcessor());
resolvers.add(new ErrorsMethodArgumentResolver());
resolvers.add(new SessionStatusMethodArgumentResolver());
resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
// Custom arguments
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
// Catch-all
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));
return resolvers;
}


在这里我们可以看到:


Spring MVC本身提供了非常丰富的HandlerMethodArgumentResolver实现。
HandlerMethodArgumentResolver是按顺序执行,当然为了性能Spring本身是有Cache的,一旦确定了某一个参数可以应用的HandlerMethodArgumentResolver,下次就不会再遍历这个List了。
自定的HandlerMethodArgumentResolver会晚于Spring自己的被执行,这也是上面提到的JSONObject会被MapMethodProcessor先处理的原因。
Spring自己的JSON映射机制是通过RequestResponseBodyMethodProcessor + AllEncompassingFormHttpMessageConverter来实现的
很不幸这是一个private方法, 你没有办法简单的改变Spring MVC的默认行为,除非你重写RequestMappingHandlerAdapter


好了,有了FastJsonArgumentResolver, 接下来我们要让它生效:


Xml代码 收藏代码
<mvc:annotation-driven>
<mvc:argument-resolvers>
<beans:bean class="org.springframework.samples.mvc.fastjson.FastJsonArgumentResolver"/>
</mvc:argument-resolvers>
<mvc:message-converters>
<beans:bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>


就是个Spring的配置,这里就不多讲了。除了FastJsonArgumentResolver,我们还配置了FastJsonHttpMessageConverter来对返回值进行序列化。
本来我是想自己写一个FastJsonHttpMessageConverter, 后来发现fastjson库里已经存在了, 我就不自己造轮子了。我们自己来看看实现吧,截取了一部分:


Java代码 收藏代码
@Override
protected void writeInternal(Object obj, HttpOutputMessage outputMessage) throws IOException,
HttpMessageNotWritableException {
OutputStream out = outputMessage.getBody();
String text = JSON.toJSONString(obj, features);
byte[] bytes = text.getBytes(charset);
out.write(bytes);
}


实现很简单, 就不详细说了。
最后来看看如何通过Ajax调用上面的Action方法:


Java代码 收藏代码
var data = {
name : "matianyi",
id : 12345,
birthday : "1983-07-01 01:12:12",
addresses : [
{
street : "street1",
number : 1
},
{
street : "street2",
number : 2
}
]
};
var link = $(this);
$.ajax({
url:"/spring-sample/fastjson1",
dataType:"json",
type:"POST",
contentType: "application/json",
data : JSON.stringify(data),
success : function(obj){
console.log(obj);
}
});


两点需要注意:


contentType: "application/json"
data : JSON.stringify(data)


这样JavaScript的对象会被转换为JSON串,并且最为HttpRequest的BODY传给服务器。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
14天前
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
40 2
|
2月前
|
JSON 前端开发 Java
SSM:SpringMVC
本文介绍了SpringMVC的依赖配置、请求参数处理、注解开发、JSON处理、拦截器、文件上传下载以及相关注意事项。首先,需要在`pom.xml`中添加必要的依赖,包括Servlet、JSTL、Spring Web MVC等。接着,在`web.xml`中配置DispatcherServlet,并设置Spring MVC的相关配置,如组件扫描、默认Servlet处理器等。然后,通过`@RequestMapping`等注解处理请求参数,使用`@ResponseBody`返回JSON数据。此外,还介绍了如何创建和配置拦截器、文件上传下载的功能,并强调了JSP文件的放置位置,避免404错误。
|
2月前
|
前端开发 Java 应用服务中间件
【Spring】Spring MVC的项目准备和连接建立
【Spring】Spring MVC的项目准备和连接建立
60 2
|
3月前
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
|
2月前
|
XML 前端开发 Java
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
本文阐述了Spring、Spring Boot和Spring MVC的关系与区别,指出Spring是一个轻量级、一站式、模块化的应用程序开发框架,Spring MVC是Spring的一个子框架,专注于Web应用和网络接口开发,而Spring Boot则是对Spring的封装,用于简化Spring应用的开发。
161 0
Spring,SpringBoot和SpringMVC的关系以及区别 —— 超准确,可当面试题!!!也可供零基础学习
|
3月前
|
XML 缓存 前端开发
springMVC02,restful风格,请求转发和重定向
文章介绍了RESTful风格的基本概念和特点,并展示了如何使用SpringMVC实现RESTful风格的请求处理。同时,文章还讨论了SpringMVC中的请求转发和重定向的实现方式,并通过具体代码示例进行了说明。
springMVC02,restful风格,请求转发和重定向
|
4月前
|
Java 数据库连接 Spring
后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】
文章是关于Spring、SpringMVC、Mybatis三个后端框架的超详细入门教程,包括基础知识讲解、代码案例及SSM框架整合的实战应用,旨在帮助读者全面理解并掌握这些框架的使用。
后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】
|
4月前
|
XML JSON 数据库
SpringMVC入门到实战------七、RESTful的详细介绍和使用 具体代码案例分析(一)
这篇文章详细介绍了RESTful的概念、实现方式,以及如何在SpringMVC中使用HiddenHttpMethodFilter来处理PUT和DELETE请求,并通过具体代码案例分析了RESTful的使用。
SpringMVC入门到实战------七、RESTful的详细介绍和使用 具体代码案例分析(一)
|
4月前
|
前端开发 应用服务中间件 数据库
SpringMVC入门到实战------八、RESTful案例。SpringMVC+thymeleaf+BootStrap+RestFul实现员工信息的增删改查
这篇文章通过一个具体的项目案例,详细讲解了如何使用SpringMVC、Thymeleaf、Bootstrap以及RESTful风格接口来实现员工信息的增删改查功能。文章提供了项目结构、配置文件、控制器、数据访问对象、实体类和前端页面的完整源码,并展示了实现效果的截图。项目的目的是锻炼使用RESTful风格的接口开发,虽然数据是假数据并未连接数据库,但提供了一个很好的实践机会。文章最后强调了这一章节主要是为了练习RESTful,其他方面暂不考虑。
SpringMVC入门到实战------八、RESTful案例。SpringMVC+thymeleaf+BootStrap+RestFul实现员工信息的增删改查
|
4月前
|
JSON 前端开发 Java
Spring MVC返回JSON数据
综上所述,Spring MVC提供了灵活、强大的方式来支持返回JSON数据,从直接使用 `@ResponseBody`及 `@RestController`注解,到通过配置消息转换器和异常处理器,开发人员可以根据具体需求选择合适的实现方式。
181 4