接上一篇:spring boot 是如何利用jackson进行反序列化的?

@RestController
public class HelloController { @RequestMapping("/")
public BillSearch hello(@RequestBody BillSearch search) { return search;
}
}

返回的search是如何序列化json的?

上一篇说到RequestResponseBodyMethodProcessor这个类在json序列化和反序列化都中很重要:

protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException { HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
Assert.state(servletRequest != null, "No HttpServletRequest");
ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest); Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
if (arg == null && checkRequired(parameter)) {
throw new HttpMessageNotReadableException("Required request body is missing: " +
parameter.getExecutable().toGenericString(), inputMessage);
}
return arg;
}

上面是反序列化时用的。

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { mavContainer.setRequestHandled(true);
ServletServerHttpRequest inputMessage = createInputMessage(webRequest);
ServletServerHttpResponse outputMessage = createOutputMessage(webRequest); // Try even with null return value. ResponseBodyAdvice could get involved.
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}

上面是序列化时用到的。

当然也可以F7一点一点的debug也会进入到这里。

然后继续:

for (HttpMessageConverter<?> converter : this.messageConverters) {
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
(GenericHttpMessageConverter<?>) converter : null);
if (genericConverter != null ?
((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
converter.canWrite(valueType, selectedMediaType)) {
body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
(Class<? extends HttpMessageConverter<?>>) converter.getClass(),
inputMessage, outputMessage);
if (body != null) {
Object theBody = body;
LogFormatUtils.traceDebug(logger, traceOn ->
"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
addContentDispositionHeader(inputMessage, outputMessage);
if (genericConverter != null) {
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
}
else {
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Nothing to write: null body");
}
}
return;
}
}

又走了这样的循环城,上篇中也介绍到了类型的循环,它就是要找到个们的jackson:

org.springframework.http.converter.json.MappingJackson2HttpMessageConverter

下面的步骤太多,我就把调用的堆栈截图出来:

看第一行,又是BeanSerielizer

public final void serialize(Object bean, JsonGenerator gen, SerializerProvider provider)
throws IOException
{
if (_objectIdWriter != null) {
gen.setCurrentValue(bean); // [databind#631]
_serializeWithObjectId(bean, gen, provider, true);
return;
}
gen.writeStartObject(bean);
if (_propertyFilterId != null) {
serializeFieldsFiltered(bean, gen, provider);
} else {
serializeFields(bean, gen, provider);
}
gen.writeEndObject();
}

开始序列化每个属性:

protected void serializeFields(Object bean, JsonGenerator gen, SerializerProvider provider)
throws IOException
{
final BeanPropertyWriter[] props;
if (_filteredProps != null && provider.getActiveView() != null) {
props = _filteredProps;
} else {
props = _props;
}
int i = 0;
try {
for (final int len = props.length; i < len; ++i) {
BeanPropertyWriter prop = props[i];
if (prop != null) { // can have nulls in filtered list
prop.serializeAsField(bean, gen, provider);
}
}
if (_anyGetterWriter != null) {
_anyGetterWriter.getAndSerialize(bean, gen, provider);
}
} catch (Exception e) {
String name = (i == props.length) ? "[anySetter]" : props[i].getName();
wrapAndThrow(provider, e, bean, name);
} catch (StackOverflowError e) {
// 04-Sep-2009, tatu: Dealing with this is tricky, since we don't have many
// stack frames to spare... just one or two; can't make many calls. // 10-Dec-2015, tatu: and due to above, avoid "from" method, call ctor directly:
//JsonMappingException mapE = JsonMappingException.from(gen, "Infinite recursion (StackOverflowError)", e);
JsonMappingException mapE = new JsonMappingException(gen, "Infinite recursion (StackOverflowError)", e); String name = (i == props.length) ? "[anySetter]" : props[i].getName();
mapE.prependPath(new JsonMappingException.Reference(bean, name));
throw mapE;
}
}
public void serializeAsField(Object bean, JsonGenerator gen,
SerializerProvider prov) throws Exception {
// inlined 'get()'
final Object value = (_accessorMethod == null) ? _field.get(bean)
: _accessorMethod.invoke(bean, (Object[]) null); // Null handling is bit different, check that first
if (value == null) {
if (_nullSerializer != null) {
gen.writeFieldName(_name);
_nullSerializer.serialize(null, gen, prov);
}
return;
}
// then find serializer to use
JsonSerializer<Object> ser = _serializer;
if (ser == null) {
Class<?> cls = value.getClass();
PropertySerializerMap m = _dynamicSerializers;
ser = m.serializerFor(cls);
if (ser == null) {
ser = _findAndAddDynamic(m, cls, prov);
}
}
// and then see if we must suppress certain values (default, empty)
if (_suppressableValue != null) {
if (MARKER_FOR_EMPTY == _suppressableValue) {
if (ser.isEmpty(prov, value)) {
return;
}
} else if (_suppressableValue.equals(value)) {
return;
}
}
// For non-nulls: simple check for direct cycles
if (value == bean) {
// three choices: exception; handled by call; or pass-through
if (_handleSelfReference(bean, gen, prov, ser)) {
return;
}
}
gen.writeFieldName(_name);
if (_typeSerializer == null) {
ser.serialize(value, gen, prov);
} else {
ser.serializeWithType(value, gen, prov, _typeSerializer);
}
}

这里用到的序列化的类是com.fasterxml.jackson.databind.ser.std.EnumSerializer,是不是和上一篇很像,其实序列化和反序列化的原理和过程真的很像。

这里由于属性是用的是枚举,但序列化后是汉字:

如果想序列化成数字呢?

我的枚举是这样的:

@Getter
@AllArgsConstructor
//@JsonDeserialize(using = BaseEnumDeserializer.class)
public enum EnumBookingState implements BaseEnum { 订购中(1),
订购完成(2);
//
//@JsonValue
private final int value; }

很简单,只需要在value上中上@JsonValue,但它的原理是什么呢?

同样是上面的截图,但是序列化的工具就是不一样了:

public void serialize(Object bean, JsonGenerator gen, SerializerProvider prov) throws IOException
{
try {
Object value = _accessor.getValue(bean);
if (value == null) {
prov.defaultSerializeNull(gen);
return;
}
JsonSerializer<Object> ser = _valueSerializer;
if (ser == null) {
Class<?> c = value.getClass();
/* 10-Mar-2010, tatu: Ideally we would actually separate out type
* serializer from value serializer; but, alas, there's no access
* to serializer factory at this point...
*/
// let's cache it, may be needed soon again
ser = prov.findTypedValueSerializer(c, true, _property);
}
ser.serialize(value, gen, prov);
} catch (Exception e) {
wrapAndThrow(prov, e, bean, _accessor.getName() + "()");
}
}

为什么打上了标签就用的是JsonValueSerializer?

protected final JsonSerializer<?> findSerializerByAnnotations(SerializerProvider prov,
JavaType type, BeanDescription beanDesc)
throws JsonMappingException
{
Class<?> raw = type.getRawClass();
// First: JsonSerializable?
if (JsonSerializable.class.isAssignableFrom(raw)) {
return SerializableSerializer.instance;
}
// Second: @JsonValue for any type
AnnotatedMember valueAccessor = beanDesc.findJsonValueAccessor();
if (valueAccessor != null) {
if (prov.canOverrideAccessModifiers()) {
ClassUtil.checkAndFixAccess(valueAccessor.getMember(),
prov.isEnabled(MapperFeature.OVERRIDE_PUBLIC_ACCESS_MODIFIERS));
}
JsonSerializer<Object> ser = findSerializerFromAnnotation(prov, valueAccessor);
return new JsonValueSerializer(valueAccessor, ser);
}
// No well-known annotations...
return null;
}

具体的调用调用太多,还是把堆栈信息拿出来:

整个的调用过程与反序列化的过程几乎一样。

下面这个类是一个主要的过程 :

protected JsonSerializer<?> _createSerializer2(SerializerProvider prov,
JavaType type, BeanDescription beanDesc, boolean staticTyping)
throws JsonMappingException
{
JsonSerializer<?> ser = null;
final SerializationConfig config = prov.getConfig(); // Container types differ from non-container types
// (note: called method checks for module-provided serializers)
if (type.isContainerType()) {
if (!staticTyping) {
staticTyping = usesStaticTyping(config, beanDesc, null);
}
// 03-Aug-2012, tatu: As per [databind#40], may require POJO serializer...
ser = buildContainerSerializer(prov, type, beanDesc, staticTyping);
// Will return right away, since called method does post-processing:
if (ser != null) {
return ser;
}
} else {
if (type.isReferenceType()) {
ser = findReferenceSerializer(prov, (ReferenceType) type, beanDesc, staticTyping);
} else {
// Modules may provide serializers of POJO types:
for (Serializers serializers : customSerializers()) {
ser = serializers.findSerializer(config, type, beanDesc);
if (ser != null) {
break;
}
}
}
// 25-Jun-2015, tatu: Then JsonSerializable, @JsonValue etc. NOTE! Prior to 2.6,
// this call was BEFORE custom serializer lookup, which was wrong.
if (ser == null) {
ser = findSerializerByAnnotations(prov, type, beanDesc);
}
} if (ser == null) {
// Otherwise, we will check "primary types"; both marker types that
// indicate specific handling (JsonSerializable), or main types that have
// precedence over container types
ser = findSerializerByLookup(type, config, beanDesc, staticTyping);
if (ser == null) {
ser = findSerializerByPrimaryType(prov, type, beanDesc, staticTyping);
if (ser == null) {
// And this is where this class comes in: if type is not a
// known "primary JDK type", perhaps it's a bean? We can still
// get a null, if we can't find a single suitable bean property.
ser = findBeanSerializer(prov, type, beanDesc);
// Finally: maybe we can still deal with it as an implementation of some basic JDK interface?
if (ser == null) {
ser = findSerializerByAddonType(config, type, beanDesc, staticTyping);
// 18-Sep-2014, tatu: Actually, as per [jackson-databind#539], need to get
// 'unknown' serializer assigned earlier, here, so that it gets properly
// post-processed
if (ser == null) {
ser = prov.getUnknownTypeSerializer(beanDesc.getBeanClass());
}
}
}
}
}
if (ser != null) {
// [databind#120]: Allow post-processing
if (_factoryConfig.hasSerializerModifiers()) {
for (BeanSerializerModifier mod : _factoryConfig.serializerModifiers()) {
ser = mod.modifySerializer(config, beanDesc, ser);
}
}
}
return ser;
}

这个方法名,就告诉我们答案了:findSerializerByAnnotations

spring boot 是如何利用jackson进行序列化的?的更多相关文章

  1. spring boot 是如何利用jackson进行反序列化的?

    以下面的代码为例: @RestController public class HelloController { @RequestMapping("/") public BillS ...

  2. Spring Boot 多站点利用 Redis 实现 Session 共享

    如何在不同站点(web服务进程)之间共享会话 Session 呢,原理很简单,就是把这个 Session 独立存储在一个地方,所有的站点都从这个地方读取 Session. 通常我们使用 Redis 来 ...

  3. springboot自定义消息转换器HttpMessageConverter Spring Boot - 使用Gson替换Jackson

    Jackson一直是springframework默认的json库,从4.1开始,springframework支持通过配置GsonHttpMessageConverter的方式使用Gson. 在典型 ...

  4. Spring Boot 2.0 利用 Spring Security 实现简单的OAuth2.0认证方式2

    0.前言 经过前面一小节已经基本配置好了基于SpringBoot+SpringSecurity+OAuth2.0的环境.这一小节主要对一些写固定InMemory的User和Client进行扩展.实现动 ...

  5. Spring Boot 2.0 利用 Spring Security 实现简单的OAuth2.0认证方式1

    0. 前言 之前帐号认证用过自己写的进行匹配,现在要学会使用标准了.准备了解和使用这个OAuth2.0协议. 1. 配置 1.1 配置pom.xml 有些可能会用不到,我把我项目中用到的所有包都贴出来 ...

  6. spring boot 配置 fastjson 替代 Jackson (并解决返回字符串带双引号问题)

    注:以我遇到的情况,只要发出的请求参数是map格式的,都会在前后多加一个双引号 以下代码有两个功能:1.FastJson 替换 Spring 自带的 Jackson  2.解决返回的字符串带双引号问题 ...

  7. Spring Boot(四):利用devtools实现热部署,改动代码自动生效

    一.前言 spring-boot-devtools是一个为开发者服务的一个模块,其中最重要的功能就是自动应用代码更改到最新的App上面去.原理是在发现代码有更改之后,重新启动应用,但是速度比手动停止后 ...

  8. Spring Boot + Redis实战-利用自定义注解+分布式锁实现接口幂等性

    场景 不管是传统行业还是互联网行业,我们都需要保证大部分操作是幂等性的,简单点说,就是无论用户点击多少次,操作多少遍,产生的结果都是一样的,是唯一的.而今次公司的项目里,又被我遇到了这么一个幂等性的问 ...

  9. Spring Boot Json 之 Jackjson Fastjson

    Json 是目前互联网应用使用最为广泛的信息交换格式之一.Spring Boot 内置了 Jackson .Json 在应用中主要体现在以下功能: 序列化 反序列化 字段格式化 验证自动化 目前长用的 ...

随机推荐

  1. Spark译文(三)

    Structured Streaming Programming Guide(结构化流编程指南) Overview(概貌) ·Structured Streaming是一种基于Spark SQL引擎的 ...

  2. [CF666E]Forensic Examination:后缀自动机+线段树合并

    分析 用到了两个小套路: 使用线段树合并维护广义后缀自动机的\(right\)集合. 查询\(S[L,R]\)在\(T\)中的出现次数:给\(T\)建SAM,在上面跑\(S\),跑到\(R\)的时候先 ...

  3. A. Be Positive

    A. Be Positive time limit per test 1 second memory limit per test 256 megabytes input standard input ...

  4. Kaggle 网站上的练习

    快速的数据科学教程: https://www.kaggle.com/learn/overview

  5. 【转】Java压缩和解压文件工具类ZipUtil

    特别提示:本人博客部分有参考网络其他博客,但均是本人亲手编写过并验证通过.如发现博客有错误,请及时提出以免误导其他人,谢谢!欢迎转载,但记得标明文章出处:http://www.cnblogs.com/ ...

  6. MQTT消息中间件Mosquitto的安装和配置

    特别提示:本人博客部分有参考网络其他博客,但均是本人亲手编写过并验证通过.如发现博客有错误,请及时提出以免误导其他人,谢谢!欢迎转载,但记得标明文章出处:http://www.cnblogs.com/ ...

  7. 【CentOS】yum安装教训

    前言:本来想安装sl在新安装的centos7上,网上搜了教程,很多都是先要你yum -y update,如下: 1.更新yum源: yum -y update 2.依赖安装: wget http:// ...

  8. 云服务器搭建anaconda pytorch torchvision

    (因为在普通用户上安装有些权限问题安装出错,所以我在root用户下相对容易安装,但是anaconda官网说可以直接在普通用户下安装,不过,在root下安装,其他用户也是能用的. 访问Anaconda官 ...

  9. java高级面试题汇总(复习)从最基础的往上复习,每天定期更新。

    每天搬一点砖,总有一天成为大牛! 看问题的时候请不要立马去翻答案,多想想. 看完答案可以问问为什么,尝试拓展!一起加油吧! 每个答案后面都有一个小彩蛋(一个以上的拓展问题),钻研让你先人一步. jav ...

  10. win7环境下mongodb分片和移除

    本文主要介绍在一台win7电脑上模拟mongo分片.如果有多台服务器,可以将每个mongo部署在单台电脑上.我们将配置3个mongo分片,3个配置服务器,1个路由服务器.如下图所示进行配置,介绍如何增 ...