1 Star 0 Fork 165

ElonChung / Java-Review

forked from flatfish / Java-Review 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
SpringBoot整合fastjson和jackson.md 18.07 KB
一键复制 编辑 原始数据 按行查看 历史
icanci 提交于 2020-09-07 22:43 . :fire:更新文件名

SpringBoot 整合 fastjson 和 jackson

jackson 和 fastJson 的对比

有很多朋友习惯于使用阿里巴巴的 fastJson 来做项目中 json 转换的相关工作,目前我们项目中使用的就是阿里的 fastJson,那么 jackson 和 fastJson 有哪些区别呢?根据网上公开的资料比较得到下表。

选项 fastJson jackson
上手难易程度 容易 中等
高级特性支持 中等 丰富
官方文档、Example支持 中文 英文
处理json速度 略快

关于 fastJson 和 jackson 的对比,网上有很多资料可以查看,主要是根据自己实际项目情况来选择合适的框架。从扩展上来看,fastJson 没有 jackson 灵活,从速度或者上手难度来看,fastJson 可以考虑。

SpringBoot 整合 jackson

在项目开发中,接口与接口之间,前后端之间数据的传输都使用 Json 格式,在 Spring Boot 中,接口返回 Json 格式的数据很简单,在 Controller 中使用@RestController注解即可返回 Json 格式的数据,@RestController也是 Spring Boot 新增的一个注解,我们点进去看一下该注解都包含了哪些东西。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {

	/**
	 * The value may indicate a suggestion for a logical component name,
	 * to be turned into a Spring bean in case of an autodetected component.
	 * @return the suggested component name, if any (or empty String otherwise)
	 * @since 4.0.1
	 */
	@AliasFor(annotation = Controller.class)
	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.3.0.RELEASE</version>
     <scope>compile</scope>
</dependency>

Spring Boot 中对依赖都做了很好的封装,可以看到很多 spring-boot-starter-xxx 系列的依赖,这是 Spring Boot 的特点之一,不需要人为去引入很多相关的依赖了,starter-xxx 系列直接都包含了所必要的依赖,所以我们再次点进去上面这个 spring-boot-starter-json 依赖,可以看到:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
        <version>2.3.0.RELEASE</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jdk8</artifactId>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.module</groupId>
        <artifactId>jackson-module-parameter-names</artifactId>
        <scope>compile</scope>
    </dependency>
</dependencies>

到此为止,我们知道了 Spring Boot 中默认使用的 json 解析框架是 jackson。下面我们看一下默认的 jackson 框架对常用数据类型的转 Json 处理。

创建User实体类

为了测试,这里创建一个User实体类

其中@Date是Lombok注解 用来生成get、set、toString等方法 注意,在使用Lombok注解的时候,需要手动添加无参构造方法, 否则会报错无法创建 bean

@Component
@Data
public class User {
    private String username;
    private String password;

    public User() {
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
}
创建Controller类

创建一个UserController 用来测试返回的结果分别为User对象 List<User> 对象 Map<User,Object>对象

@RestController
@RequestMapping("/jackson")
public class UserController {

    @RequestMapping("/user")
    public User user() {
        return new User("icanci", "hashmap");
    }

    @RequestMapping("/list")
    public List<User> list() {
        ArrayList<User> users = new ArrayList<>();
        users.add(new User("icanci", "hashmap"));
        users.add(new User("icanci1", "hashmap1"));
        users.add(new User("icanci2", "hashmap2"));
        users.add(new User("icanci3", "hashmap3"));
        users.add(new User("icanci4", "hashmap4"));
        return users;
    }

    @RequestMapping("/map")
    public Map<User, Object> map() {
        HashMap<User, Object> map = new HashMap<>();
        map.put(new User("icanci", "hashmap"), "1");
        map.put(new User("icanci1", "hashmap1"), "2");
        map.put(new User("icanci2", "hashmap2"), "3");
        map.put(new User("icanci3", "hashmap3"), "4");
        map.put(new User("icanci4", "hashmap4"), "5");
        return map;
    }
}
测试浏览器返回的Json数据

在浏览器输入http://localhost:8080/jackson/user

返回数据如下

{
	"username": "icanci",
	"password": "hashmap"
}

在浏览器输入http://localhost:8080/jackson/list

返回数据如下

[
    {
        "username": "icanci",
        "password": "hashmap"
    },
    {
        "username": "icanci1",
        "password": "hashmap1"
    },
    {
        "username": "icanci2",
        "password": "hashmap2"
    },
    {
        "username": "icanci3",
        "password": "hashmap3"
    },
    {
        "username": "icanci4",
        "password": "hashmap4"
    }
]

在浏览器输入 http://localhost:8080/jackson/map

返回的数据如下

{
    "User(username=icanci2, password=hashmap2)": "3",
    "User(username=icanci3, password=hashmap3)": "4",
    "User(username=icanci4, password=hashmap4)": "5",
    "User(username=icanci, password=hashmap)": "1",
    "User(username=icanci1, password=hashmap1)": "2"
}

由上述结果即可看出,无论浏览器输入怎样的请求,都可以返回对应的 Json 数据

Jackson对null的处理

在实际项目中,我们难免会遇到一些 null 值出现,我们转 json 时,是不希望有这些 null 出现的,比如我们期望所有的 null 在转 json 时都变成 "" 这种空字符串,那怎么做呢?

# 修改之后返回的JSON数据
[
    {
        "username": "icanci",
        "password": "hashmap"
    },
    {
        "username": "icanci1",
        "password": "hashmap1"
    },
    {
        "username": "icanci2",
        "password": "hashmap2"
    },
    {
        "username": "icanci3",
        "password": "hashmap3"
    },
    null
]

在 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 build = builder.createXmlMapper(false).build();
        build.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {
            @Override
            public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                gen.writeString("");
            }
        });
        return build;
    }
}

这样,我们再修改返回map的接口再测试一下

@RequestMapping("/map")
public Map<User, Object> map() {
    HashMap<User, Object> map = new HashMap<>();
    map.put(new User("icanci", "hashmap"), null);
    map.put(new User(), "2");
    map.put(new User("icanci2", "hashmap2"), "3");
    map.put(new User("icanci3", "hashmap3"), null);
    map.put(new User("icanci4", "hashmap4"), "5");
    return map;
}

重启项目,访问地址http://localhost:8080/jackson/map

返回结果如下

{
    "User(username=icanci2, password=hashmap2)": "3",
    "User(username=icanci3, password=hashmap3)": "",
    "User(username=icanci4, password=hashmap4)": "5",
    "User(username=icanci, password=hashmap)": "",
    "User(username=null, password=null)": "2"
}

SpringBoot 整合 fastjson

前段时间,阿里巴巴的 fastjson 出现了重大Bug,一时间传的沸沸扬扬,但是再中国它依旧很火。

使用 fastJson 需要导入依赖,本教程使用 1.2.35 版本,依赖如下:

<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>fastjson</artifactId>
	<version>1.2.35</version>
</dependency>

使用 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.WebMvcConfigurer;

import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

@Configuration
public class MyFastJsonConfig 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);
    }
}

注意 彩坑记录:这里进行类命名的时候,不可以使用 FastJsonConfig 因为 FastJsonConfig 是阿里巴巴 FastJson 的一个配置类,我把自己的命名为这个,导致方法找不出来,所以我修改命名为MyFastJsonConfig 即可

但是我本地测试,发现界面依旧显示的是null,而非是空,问题暂未解决

封装统一返回的数据结构

以上是 Spring Boot 返回 json 的几个代表的例子,但是在实际项目中,除了要封装数据之外,我们往往需要在返回的 json 中添加一些其他信息,比如返回一些状态码 code ,返回一些 msg 给调用者,这样调用者可以根据 code 或者 msg 做一些逻辑判断。所以在实际项目中,我们需要封装一个统一的 json 返回结构存储返回信息。

定义统一的 json 结构

由于封装的 json 数据的类型不确定,所以在定义统一的 json 结构时,我们需要用到泛型。统一的 json 结构中属性包括数据、状态码、提示信息即可,构造方法可以根据实际业务需求做相应的添加即可,一般来说,应该有默认的返回结构,也应该有用户指定的返回结构。如下:

@Component
@Getter
@Setter
@ToString
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;
    }
    
    /**
     * 有数据返回,状态码人为设置,人为指定提示信息
     *
     * @param data
     * @param msg
     * @param code
     */
    public JsonResult(T data, String msg,String code) {
        this.data = data;
        this.code = code;
        this.msg = msg;
    }
}
增加一个HelloController测试统一封装的结果

由于 JsonResult 使用了泛型,所以所有的返回值类型都可以使用该统一结构,在具体的场景将泛型替换成具体的数据类型即可,非常方便,也便于维护。在实际项目中,还可以继续封装,比如状态码和提示信息可以定义一个枚举类型,以后我们只需要维护这个枚举类型中的数据即可(在本教程中就不展开了)。根据以上的 JsonResult,我们改写一下 Controller,如下:

@RestController
@RequestMapping("/hello")
public class HelloController {
    @RequestMapping("/user")
    public JsonResult<User> user() {
        return new JsonResult<>(new User("icanci", "hashmap"),"success","200");
    }

    @RequestMapping("/list")
    public JsonResult< List<User>> list() {
        ArrayList<User> users = new ArrayList<>();
        users.add(new User("icanci", "hashmap"));
        users.add(new User("icanci1", "hashmap1"));
        users.add(new User("icanci2", "hashmap2"));
        users.add(new User("icanci3", "hashmap3"));
        users.add(new User("icanci4", "hashmap4"));
        users.add(null);
        return new JsonResult<>(users,"获取用户列表成功");
    }

    @RequestMapping("/map")
    public JsonResult<Map<User, Object>> map() {
        HashMap<User, Object> map = new HashMap<>();
        map.put(new User("icanci", "hashmap"), null);
        map.put(new User(), "2");
        map.put(new User("icanci2", "hashmap2"), "3");
        map.put(new User("icanci3", "hashmap3"), null);
        map.put(new User("icanci4", "hashmap4"), "5");
        return new JsonResult<>(map);
    }
}

浏览器输入http://localhost:8080/hello/user

返回结果如下

{
    "data": {
    "username": "icanci",
    "password": "hashmap"
	},
    "code": "200",
    "msg": "success"
}

浏览器输入http://localhost:8080/hello/list

返回结果如下

{
    "data": [
    {
        "username": "icanci",
        "password": "hashmap"
    },
    {
        "username": "icanci1",
        "password": "hashmap1"
    },
    {
        "username": "icanci2",
        "password": "hashmap2"
    },
    {
        "username": "icanci3",
        "password": "hashmap3"
    },
    {
        "username": "icanci4",
        "password": "hashmap4"
    },
    ""
    ],
    "code": "0",
    "msg": "获取用户列表成功"
}

浏览器输入http://localhost:8080/hello/map

返回结果如下

{
    "data": {
        "User(username=icanci2, password=hashmap2)": "3",
        "User(username=icanci3, password=hashmap3)": "",
        "User(username=icanci4, password=hashmap4)": "5",
        "User(username=icanci, password=hashmap)": "",
        "User(username=null, password=null)": "2"
    },
    "code": "0",
    "msg": "操作成功!"
}

通过封装,我们不但将数据通过 json 传给前端或者其他接口,还带上了状态码和提示信息,这在实际项目场景中应用非常广泛。

总结

本节主要对 Spring Boot 中 json 数据的返回做了详细的分析,从 Spring Boot 默认的 jackson 框架到阿里巴巴的 fastJson 框架,分别对它们的配置做了相应的讲解。另外,结合实际项目情况,总结了实际项目中使用的 json 封装结构体,加入了状态码和提示信息,使得返回的 json 数据信息更加完整。

1
https://gitee.com/elonchung/Java-Review.git
git@gitee.com:elonchung/Java-Review.git
elonchung
Java-Review
Java-Review
master

搜索帮助