在項目開發中,接口與接口之間,前后端之間數據的傳輸都使用 Json 格式,在 Spring Boot 中,接口返回 Json 格式的數據很簡單,在 Controller 中使用@RestController
注解即可返回 Json 格式的數據,@RestController
也是 Spring Boot 新增的一個注解,我們點進去看一下該注解都包含了哪些東西。
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Controller @ResponseBody public @interface RestController { String value() default ""; }
可以看出, @RestController
注解包含了原來的 @Controller
和 @ResponseBody
注解,使用過 Spring 的朋友對 @Controller
注解已經非常了解了,這里不再贅述, @ResponseBody
注解是將返回的數據結構轉換為 Json 格式。所以在默認情況下,使用了 @RestController
注解即可將返回的數據結構轉換成 Json 格式,Spring Boot 中默認使用的 Json 解析技術框架是 jackson。我們點開 pom.xml 中的 spring-boot-starter-web
依賴,可以看到一個 spring-boot-starter-json
依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-json</artifactId> <version>2.0.3.RELEASE</version> <scope>compile</scope> </dependency>
Spring Boot 中對依賴都做了很好的封裝,可以看到很多 spring-boot-starter-xxx
系列的依賴,這是 Spring Boot 的特點之一,不需要人為去引入很多相關的依賴了,starter-xxx 系列直接都包含了所必要的依賴,所以我們再次點進去上面這個 spring-boot-starter-json
依賴,可以看到:
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.6</version> <scope>compile</scope> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jdk8</artifactId> <version>2.9.6</version> <scope>compile</scope> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId> <version>2.9.6</version> <scope>compile</scope> </dependency> <dependency> <groupId>com.fasterxml.jackson.module</groupId> <artifactId>jackson-module-parameter-names</artifactId> <version>2.9.6</version> <scope>compile</scope> </dependency>
到此為止,我們知道了 Spring Boot 中默認使用的 json 解析框架是 jackson。下面我們看一下默認的 jackson 框架對常用數據類型的轉 Json 處理。
1. Spring Boot 默認對Json的處理
在實際項目中,常用的數據結構無非有類對象、List對象、Map對象,我們看一下默認的 jackson 框架對這三個常用的數據結構轉成 json 后的格式如何。
1.1 創建 User 實體類
為了測試,我們需要創建一個實體類,這里我們就用 User 來演示
public class User { private Long id; private String username; private String password; /* 省略get、set和帶參構造方法 */ }
1.2 創建Controller類
然后我們創建一個 Controller,分別返回 User
對象、List<User>
和 Map<String, Object>
。
import com.itcodai.course02.entity.User; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @RestController @RequestMapping("/json") public class JsonController { @RequestMapping("/user") public User getUser() { return new User(1, "倪升武", "123456"); } @RequestMapping("/list") public List<User> getUserList() { List<User> userList = new ArrayList<>(); User user1 = new User(1, "倪升武", "123456"); User user2 = new User(2, "達人課", "123456"); userList.add(user1); userList.add(user2); return userList; } @RequestMapping("/map") public Map<String, Object> getMap() { Map<String, Object> map = new HashMap<>(3); User user = new User(1, "倪升武", "123456"); map.put("作者信息", user); map.put("博客地址", "http://blog.itcodai.com"); map.put("CSDN地址", "http://blog.csdn.net/eson_15"); map.put("粉絲數量", 4153); return map; } }
1.4 jackson 中對null的處理
在實際項目中,我們難免會遇到一些 null 值出現,我們轉 json 時,是不希望有這些 null 出現的,比如我們期望所有的 null 在轉 json 時都變成 "" 這種空字符串,那怎么做呢?在 Spring Boot 中,我們做一下配置即可,新建一個 jackson 的配置類:
import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializerProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import java.io.IOException; @Configuration public class JacksonConfig { @Bean @Primary @ConditionalOnMissingBean(ObjectMapper.class) public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) { ObjectMapper objectMapper = builder.createXmlMapper(false).build(); objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() { @Override public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { jsonGenerator.writeString(""); } }); return objectMapper; } }
然后我們修改一下上面返回 map 的接口,將幾個值改成 null 測試一下:
@RequestMapping("/map") public Map<String, Object> getMap() { Map<String, Object> map = new HashMap<>(3); User user = new User(1, "倪升武", null); map.put("作者信息", user); map.put("博客地址", "http://blog.itcodai.com"); map.put("CSDN地址", null); map.put("粉絲數量", 4153); return map; }
重啟項目,再次輸入:localhost:8080/json/map
,可以看到 jackson 已經將所有 null 字段轉成了空字符串了。
{"作者信息":{"id":1,"username":"倪升武","password":""},"CSDN地址":"","粉絲數量":4153,"博客地址":"http://blog.itcodai.com"}
2. 使用阿里巴巴FastJson的設置
2.2 fastJson依賴導入
使用 fastJson 需要導入依賴,本課程使用 1.2.35 版本,依賴如下:
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.35</version> </dependency>
2.2 使用 fastJson 處理 null
使用 fastJson 時,對 null 的處理和 jackson 有些不同,需要繼承 WebMvcConfigurationSupport
類,然后覆蓋 configureMessageConverters
方法,在方法中,我們可以選擇對要實現 null 轉換的場景,配置好即可。如下:
import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.support.config.FastJsonConfig; import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; import org.springframework.context.annotation.Configuration; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; @Configuration public class fastJsonConfig extends WebMvcConfigurationSupport { /** * 使用阿里 FastJson 作為JSON MessageConverter * @param converters */ @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); FastJsonConfig config = new FastJsonConfig(); config.setSerializerFeatures( // 保留map空的字段 SerializerFeature.WriteMapNullValue, // 將String類型的null轉成"" SerializerFeature.WriteNullStringAsEmpty, // 將Number類型的null轉成0 SerializerFeature.WriteNullNumberAsZero, // 將List類型的null轉成[] SerializerFeature.WriteNullListAsEmpty, // 將Boolean類型的null轉成false SerializerFeature.WriteNullBooleanAsFalse, // 避免循環引用 SerializerFeature.DisableCircularReferenceDetect); converter.setFastJsonConfig(config); converter.setDefaultCharset(Charset.forName("UTF-8")); List<MediaType> mediaTypeList = new ArrayList<>(); // 解決中文亂碼問題,相當於在Controller上的@RequestMapping中加了個屬性produces = "application/json" mediaTypeList.add(MediaType.APPLICATION_JSON); converter.setSupportedMediaTypes(mediaTypeList); converters.add(converter); } }
3. 封裝統一返回的數據結構
3.1 定義統一的 json 結構
由於封裝的 json 數據的類型不確定,所以在定義統一的 json 結構時,我們需要用到泛型。統一的 json 結構中屬性包括數據、狀態碼、提示信息即可,構造方法可以根據實際業務需求做相應的添加即可,一般來說,應該有默認的返回結構,也應該有用戶指定的返回結構。如下:
public class JsonResult<T> { private T data; private String code; private String msg; /** * 若沒有數據返回,默認狀態碼為0,提示信息為:操作成功! */ public JsonResult() { this.code = "0"; this.msg = "操作成功!"; } /** * 若沒有數據返回,可以人為指定狀態碼和提示信息 * @param code * @param msg */ public JsonResult(String code, String msg) { this.code = code; this.msg = msg; } /** * 有數據返回時,狀態碼為0,默認提示信息為:操作成功! * @param data */ public JsonResult(T data) { this.data = data; this.code = "0"; this.msg = "操作成功!"; } /** * 有數據返回,狀態碼為0,人為指定提示信息 * @param data * @param msg */ public JsonResult(T data, String msg) { this.data = data; this.code = "0"; this.msg = msg; } // 省略get和set方法 }
3.2 修改 Controller 中的返回值類型及測試
由於 JsonResult 使用了泛型,所以所有的返回值類型都可以使用該統一結構,在具體的場景將泛型替換成具體的數據類型即可,非常方便,也便於維護。在實際項目中,還可以繼續封裝,比如狀態碼和提示信息可以定義一個枚舉類型,以后我們只需要維護這個枚舉類型中的數據即可(在本課程中就不展開了)。根據以上的 JsonResult,我們改寫一下 Controller,如下:
@RestController @RequestMapping("/jsonresult") public class JsonResultController { @RequestMapping("/user") public JsonResult<User> getUser() { User user = new User(1, "倪升武", "123456"); return new JsonResult<>(user); } @RequestMapping("/list") public JsonResult<List> getUserList() { List<User> userList = new ArrayList<>(); User user1 = new User(1, "倪升武", "123456"); User user2 = new User(2, "達人課", "123456"); userList.add(user1); userList.add(user2); return new JsonResult<>(userList, "獲取用戶列表成功"); } @RequestMapping("/map") public JsonResult<Map> getMap() { Map<String, Object> map = new HashMap<>(3); User user = new User(1, "倪升武", null); map.put("作者信息", user); map.put("公眾號", "武哥聊編程"); map.put("CSDN地址", null); map.put("粉絲數量", 4153); return new JsonResult<>(map); } }
{"code":"0","data":{"id":1,"password":"123456","username":"倪升武"},"msg":"操作成功!"} {"code":"0","data":[{"id":1,"password":"123456","username":"倪升武"},{"id":2,"password":"123456","username":"達人課"}],"msg":"獲取用戶列表成功"} {"code":"0","data":{"作者信息":{"id":1,"password":"","username":"倪升武"},"CSDN地址":null,"粉絲數量":4153,"公眾號":"武哥聊編程"},"msg":"操作成功!"}