來源: https://www.cnblogs.com/daxin/p/3296493.html 自定義Spring MVC3的參數(shù)映射和返回值映射 fastjson首先說一下場景:在一些富客戶端Web應(yīng)用程序中我們會有比較多的Ajax調(diào)用,并且希望與服務(wù)器交互的數(shù)據(jù)需要是復(fù)雜的JSON對象。 fastjon是一個非常高效的JSON序列化和反序列化庫,我希望我們輸入的JSON串能通過fastjson直接反序列化為一個復(fù)雜的JavaBean對象,同時我的返回值能夠能通過fastjson序列化為JSON串。所謂復(fù)雜的JavaBean就是,不僅僅只有一層屬性,而是屬性也是JavaBean的情況, 例如: 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 '}'; } } 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 '}'; } } ? 當(dāng)然,結(jié)構(gòu)還可以再復(fù)雜,Adress對象里還可以又復(fù)雜JavaBean的屬性。 在SpringMVC3中我們可以把輸入簡單的映射為某個Action方法的參數(shù), 例如: ? @RequestMapping(value="/someAction", method=RequestMethod.POST) public String processSubmit(FooBean fooBean, Model model) { // 利用fooBean return “views/some_page”; } ? 用Spring MVC3, 我們可以把Form里的字段輕松的映射到JavaBean的屬性。 Spring MVC3 提供了豐富的參數(shù)映射機(jī)制,?詳細(xì)信息可以參見這里 同時對于Spring MVC3默認(rèn)的提供的映射機(jī)制不能涵蓋的對象,我們可以通過擴(kuò)展HandlerMethodArgumentResolver和HttpMessageConverter的機(jī)制來實(shí)現(xiàn)。 假設(shè)對于上面的FooBean, 我們有這樣一個JSON對象和它對應(yīng): ? 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。 方法的定義是這樣的: @RequestMapping(value = "/fastjson", method = RequestMethod.POST) public @ResponseBody FooBean fastjson2(@FastJson FooBean foo) { System.out.println(foo); return foo; } ? 首先這里有個@FastJson的標(biāo)注,這是我們?yōu)榱俗屪约旱腍andlerMethodArgumentResolver能夠識別這個參數(shù)是需要自己來處理而定義的一個Annotation ? @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FastJson { } ? 然后就是定義一個FastJsonArgumentResolver,來對HttpServletRequest的body進(jìn)行解析: ? 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轉(zhuǎn)換為對應(yīng)的類型 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方法的參數(shù)的類型是JSONObject這樣的原始類型的話就直接利用JSON.parseObject(sb.toString())映射過去。 但是由于JSONObject實(shí)現(xiàn)了Map結(jié)果,所以Spring MVC3的默認(rèn)處理器MapMethodProcessor會先起作用,這樣就不能正常的映射成JSONObject對象了。 沒有辦法做了一個簡單的JSONObject包裝類,以使MapMethodProcessor不能對其進(jìn)行處理。 public class JSONObjectWrapper { private JSONObject jsonObject; public JSONObjectWrapper(JSONObject jsonObject) { this.jsonObject = jsonObject; } public JSONObject getJSONObject() { return jsonObject; } } ? 這里順便提一下,Spring MVC自己的HandlerMethodArgumentResolver有哪些,并且會以什么樣的順序執(zhí)行呢? /** * 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; } ? 在這里我們可以看到:
好了,有了FastJsonArgumentResolver, 接下來我們要讓它生效: <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來對返回值進(jìn)行序列化。 本來我是想自己寫一個FastJsonHttpMessageConverter, 后來發(fā)現(xiàn)fastjson庫里已經(jīng)存在了, 我就不自己造輪子了。我們自己來看看實(shí)現(xiàn)吧,截取了一部分: ? ![]() @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); } ![]() ? 實(shí)現(xiàn)很簡單, 就不詳細(xì)說了。 最后來看看如何通過Ajax調(diào)用上面的Action方法: 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); } }); 兩點(diǎn)需要注意:
這樣JavaScript的對象會被轉(zhuǎn)換為JSON串,并且最為HttpRequest的BODY傳給服務(wù)器。 來源:http://www./content-4-161401.html |
|