這篇文章主要講 Spring MVC 如何動(dòng)態(tài)的去返回 Json 數(shù)據(jù) 在我們做 Web 接口開發(fā)的時(shí)候, 經(jīng)常會(huì)遇到這種場(chǎng)景。
創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供泊頭網(wǎng)站建設(shè)、泊頭做網(wǎng)站、泊頭網(wǎng)站設(shè)計(jì)、泊頭網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計(jì)與制作、泊頭企業(yè)網(wǎng)站模板建站服務(wù),10多年泊頭做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。
兩個(gè)請(qǐng)求,返回同一個(gè)對(duì)象,但是需要的返回字段并不相同。如以下場(chǎng)景
/** * 返回所有名稱以及Id */ @RequestMapping("list") @ResponseBody public List<Article> findAllNameAndId() { return articleService.findAll(); } /** * 返回所有目錄詳情 */ @RequestMapping("list-detail") @ResponseBody public List<Article> findAllDetail() { return articleService.findAll(); }
Spring MVC 默認(rèn)使用轉(zhuǎn)json框架是 jackson。 大家也知道, jackson 可以在實(shí)體類內(nèi)加注解,來指定序列化規(guī)則,但是那樣比較不靈活,不能實(shí)現(xiàn)我們目前想要達(dá)到的這種情況。
這篇文章主要講的就是通過自定義注解,來更加靈活,細(xì)?;刂?json 格式的轉(zhuǎn)換。
最終我們需要實(shí)現(xiàn)如下的效果:
@RequestMapping(value = "{id}", method = RequestMethod.GET) // 返回時(shí)候不包含 filter 內(nèi)的 createTime, updateTime 字段 @JSON(type = Article.class, filter="createTime,updateTime") public Article get(@PathVariable String id) { return articleService.get(id); } @RequestMapping(value="list", method = RequestMethod.GET) // 返回時(shí)只包含 include 內(nèi)的 id, name 字段 @JSON(type = Article.class , include="id,name") public List<Article> findAll() { return articleService.findAll(); }
jackson 編程式過濾字段
jackson 中, 我們可以在實(shí)體類上加上 @JsonFilter 注解,并且通過 ObjectMapper.setFilterProvider 來進(jìn)行過濾規(guī)則的設(shè)置。 這里簡(jiǎn)單介紹一下 setFilterProvider 的使用
@JsonFilter("ID-TITLE") class Article { private String id; private String title; private String content; // ... getter/setter } // Demo class Demo { public void main(String args[]) { ObjectMapper mapper = new ObjectMapper(); // SimpleBeanPropertyFilter.filterOutAllExcept("id,title") // 過濾除了 id,title 以外的所有字段,也就是序列化的時(shí)候,只包含 id 和 title mapper.setFilterProvider(new SimpleFilterProvider().addFilter("ID-TITLE", SimpleBeanPropertyFilter.filterOutAllExcept("id,title"))); String filterOut = mapper.writeValueAsString(new Article()); mapper = new ObjectMapper(); // SimpleBeanPropertyFilter.serializeAllExcept("id,title") // 序列化所有字段,但是排除 id 和 title,也就是除了 id 和 title之外,其他字段都包含進(jìn) json mapper.setFilterProvider(new SimpleFilterProvider().addFilter("ID-TITLE", SimpleBeanPropertyFilter.serializeAllExcept(filter.split("id,title")))); String serializeAll = mapper.writeValueAsString(new Article()); System.out.println("filterOut:" + filterOut); System.out.println("serializeAll :" + serializeAll); } }
輸出結(jié)果
filterOut:{id: "", title: ""} serializeAll:{content:""}
封裝json轉(zhuǎn)換
通過上面的代碼,我們發(fā)現(xiàn),可以使用 setFilterProvider 來靈活的處理需要過濾的字段。不過上面的方法還有一些缺陷就是,還是要在 原來的 model 上加注解,這里我們使用 ObjectMapper.addMixIn(Class<?> type, Class<?> mixinType) 方法,這個(gè)方法就是講兩個(gè)類的注解混合,讓第一個(gè)參數(shù)的類能夠擁有第二個(gè)參數(shù)類的注解。讓需要過濾的 model 和 @JsonFilter 注解解除耦合
package diamond.cms.server.json; import com.fasterxml.jackson.annotation.JsonFilter; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; /** * depend on jackson * @author Diamond */ public class CustomerJsonSerializer { static final String DYNC_INCLUDE = "DYNC_INCLUDE"; static final String DYNC_FILTER = "DYNC_FILTER"; ObjectMapper mapper = new ObjectMapper(); @JsonFilter(DYNC_FILTER) interface DynamicFilter { } @JsonFilter(DYNC_INCLUDE) interface DynamicInclude { } /** * @param clazz 需要設(shè)置規(guī)則的Class * @param include 轉(zhuǎn)換時(shí)包含哪些字段 * @param filter 轉(zhuǎn)換時(shí)過濾哪些字段 */ public void filter(Class<?> clazz, String include, String filter) { if (clazz == null) return; if (include != null && include.length() > 0) { mapper.setFilterProvider(new SimpleFilterProvider().addFilter(DYNC_INCLUDE, SimpleBeanPropertyFilter.filterOutAllExcept(include.split(",")))); mapper.addMixIn(clazz, DynamicInclude.class); } else if (filter !=null && filter.length() > 0) { mapper.setFilterProvider(new SimpleFilterProvider().addFilter(DYNC_FILTER, SimpleBeanPropertyFilter.serializeAllExcept(filter.split(",")))); mapper.addMixIn(clazz, DynamicFilter.class); } } public String toJson(Object object) throws JsonProcessingException { return mapper.writeValueAsString(object); } }
我們之前的 Demo 可以變成:
// Demo class Demo { public void main(String args[]) { CustomerJsonSerializer cjs= new CustomerJsonSerializer(); // 設(shè)置轉(zhuǎn)換 Article 類時(shí),只包含 id, name cjs.filter(Article.class, "id,name", null); String include = cjs.toJson(new Article()); cjs = new CustomerJsonSerializer(); // 設(shè)置轉(zhuǎn)換 Article 類時(shí),過濾掉 id, name cjs.filter(Article.class, null, "id,name"); String filter = cjs.toJson(new Article()); System.out.println("include: " + include); System.out.println("filter: " + filter); } }
輸出結(jié)果
include: {id: "", title: ""} filter: {content:""}
自定義 @JSON 注解
我們需要實(shí)現(xiàn)文章開頭的那種效果。這里我自定義了一個(gè)注解,可以加在方法上,這個(gè)注解是用來攜帶參數(shù)給 CustomerJsonSerializer.filter 方法的,就是某個(gè)類的某些字段需要過濾或者包含。
package diamond.cms.server.json; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface JSON { Class<?> type(); String include() default ""; String filter() default ""; }
實(shí)現(xiàn) Spring MVC 的 HandlerMethodReturnValueHandler
HandlerMethodReturnValueHandler 接口 Spring MVC 用于處理請(qǐng)求返回值 。 看一下這個(gè)接口的定義和描述,接口有兩個(gè)方法supportsReturnType 用來判斷 處理類 是否支持當(dāng)前請(qǐng)求, handleReturnValue 就是具體返回邏輯的實(shí)現(xiàn)。
// Spring MVC 源碼 package org.springframework.web.method.support; import org.springframework.core.MethodParameter; import org.springframework.web.context.request.NativeWebRequest; public interface HandlerMethodReturnValueHandler { boolean supportsReturnType(MethodParameter returnType); void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception; }
我們平時(shí)使用 @ResponseBody 就是交給 RequestResponseBodyMethodProcessor 這個(gè)類處理的
還有我們返回 ModelAndView 的時(shí)候, 是由 ModelAndViewMethodReturnValueHandler 類處理的
要實(shí)現(xiàn)文章開頭的效果,我實(shí)現(xiàn)了一個(gè) JsonReturnHandler類,當(dāng)方法有 @JSON 注解的時(shí)候,使用該類來處理返回值。
package diamond.cms.server.json.spring; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.http.server.ServletServerHttpResponse; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; import diamond.cms.server.json.CustomerJsonSerializer; import diamond.cms.server.json.JSON; public class JsonReturnHandler implements HandlerMethodReturnValueHandler{ @Override public boolean supportsReturnType(MethodParameter returnType) { // 如果有我們自定義的 JSON 注解 就用我們這個(gè)Handler 來處理 boolean hasJsonAnno= returnType.getMethodAnnotation(JSON.class) != null; return hasJsonAnno; } @Override public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { // 設(shè)置這個(gè)就是最終的處理類了,處理完不再去找下一個(gè)類進(jìn)行處理 mavContainer.setRequestHandled(true); // 獲得注解并執(zhí)行filter方法 最后返回 HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class); Annotation[] annos = returnType.getMethodAnnotations(); CustomerJsonSerializer jsonSerializer = new CustomerJsonSerializer(); Arrays.asList(annos).forEach(a -> { if (a instanceof JSON) { JSON json = (JSON) a; jsonSerializer.filter(json.type(), json.include(), json.filter()); } }); response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); String json = jsonSerializer.toJson(returnValue); response.getWriter().write(json); } }
通過這些,我們就可以最終實(shí)現(xiàn)以下效果。
class Article { private String id; private String title; private String content; private Long createTime; // ... getter/setter } @Controller @RequestMapping("article") class ArticleController { @RequestMapping(value = "{id}", method = RequestMethod.GET) @JSON(type = Article.class, filter="createTime") public Article get(@PathVariable String id) { return articleService.get(id); } @RequestMapping(value="list", method = RequestMethod.GET) @JSON(type = Article.class , include="id,title") public List<Article> findAll() { return articleService.findAll(); } }
請(qǐng)求 /article/{articleId}
{ id: "xxxx", title: "xxxx", content: "xxxx" }
請(qǐng)求 article/list
[ {id: "xx", title: ""}, {id: "xx", title: ""}, {id: "xx", title: ""} ... ]
下載地址:cms-admin-end_jb51.rar
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。
網(wǎng)站名稱:SpringMVC更靈活的控制json返回問題(自定義過濾字段)
分享地址:http://chinadenli.net/article22/pghocc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計(jì)公司、軟件開發(fā)、商城網(wǎng)站、定制網(wǎng)站、網(wǎng)站排名、網(wǎng)站維護(hù)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)