Jackson原理探究—Mixins其一

使用场景

Jackson mixins 用来实现在目标类的声明以及定义的情况下,实现 Jackson 的注解添加至目标类的效果。尤其我们在使用第三方类库的时候,这种机制就会显得尤为有用。

接下来我们展示一些实际的使用场景示例:

以 spring security 为例

假设我们要反序列化这个类:

package org.springframework.security.web.csrf;

······

public final class DefaultCsrfToken implements CsrfToken {

private final String token;

private final String parameterName;

private final String headerName;

······

public DefaultCsrfToken(String headerName, String parameterName, String token) {

Assert.hasLength(headerName, "headerName cannot be null or empty");

Assert.hasLength(parameterName, "parameterName cannot be null or empty");

Assert.hasLength(token, "token cannot be null or empty");

this.headerName = headerName;

this.parameterName = parameterName;

this.token = token;

}

······

}

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No dserializer found for class org.springframework.security.web.csrf and no properties discovered to create BeanDserializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)

我们无法反序列化这两个类,因为这是一个典型的不可变类,所有属性字段在构造器中完成初始化。而jackson 默认的反序列化策略需要一个无参的构造器,并提供字段的内省 (注1) 函数。如果要更改反序列化策略,jackson 需要我们在对象上增加对应的注解。

现在让我们来创建一个 mixin 类来解决这个问题!在我们的 mixin 中,我们将声明我们反序列化所需要的注解:

package org.springframework.security.web.jackson2;

······

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")

@JsonIgnoreProperties(ignoreUnknown = true)

class DefaultCsrfTokenMixin {

······

@JsonCreator

public DefaultCsrfTokenMixin(@JsonProperty("headerName") String headerName,

@JsonProperty("parameterName") String parameterName, @JsonProperty("token") String token) {

}

}

我们创建了一个 mixin 类,并为其声明了 typeInfo 信息以及要用到的构造器信息

之后,我们需要告诉 Jackson 使用我们的 Mixin。为此,我们需要通过 jackson 的 Module 扩展机制,实现自己的扩展器,告诉 jackson 需要使用我们的 mixin:

/**

* Jackson module for spring-security-web. This module register {@link DefaultCsrfTokenMixin} and

* {@link PreAuthenticatedAuthenticationTokenMixin}. If no default typing enabled by default then it'll enable

* it because typing info is needed to properly serialize/deserialize objects.

* In order to use this module just add this module into your ObjectMapper configuration.

*

* <pre>

* ObjectMapper mapper = new ObjectMapper();

* mapper.registerModule(new WebJackson2Module());

* </pre>

* <b>Note: use {@link SecurityJackson2Modules#getModules(ClassLoader)} to get list of all security modules.</b>

*

* @author Jitendra Singh

* @see SecurityJackson2Modules

* @since 4.2

*/

public class WebJackson2Module extends SimpleModule {

public WebJackson2Module() {

super(WebJackson2Module.class.getName(), new Version(1, 0, 0, null, null, null));

}

@Override

public void setupModule(SetupContext context) {

SecurityJackson2Modules.enableDefaultTyping(context.getOwner());

context.setMixInAnnotations(DefaultCsrfToken.class, DefaultCsrfTokenMixin.class);

context.setMixInAnnotations(PreAuthenticatedAuthenticationToken.class, PreAuthenticatedAuthenticationTokenMixin.class);

}

}

完成!

这段代码中,摘取自 org.srpingframework.security:spring-security-web:5.1.5.RELEASE

这个示例中,spring security 的设计者面对了一个典型的扩展性问题:
早在接入 jackson 之前, spring security 就已经完成了 DefaultCsrfToken 的设计,但是一开始的写法却不能很好的兼容 jackson 的反序列化策略。

此时万幸,jackson 提供了 mixins 机制,这个可以支持外挂式的序列化/反序列化策略声明,从而避免了针对 spring security 核心数据结构的侵入性改变。不得不说,设计的真的很优良!

总结

mixins 机制的使用如上所述,以使用者角度来说,非常的简便,完全是声明式的。而且更重要的是,完全不需要对原来的数据结构设计做任何侵入性改变,这就太重要了,确保了我们的代码足够干净,并可以便捷开放给其他 JSON 库,例如 GSON。

那么 mixins 实际上又是如何实现的呢?请听下回分解:预留传送门

备注

注1: 内省:introspector ,java 中指代 bean 的 getter/setter 规范

以上是 Jackson原理探究—Mixins其一 的全部内容, 来源链接: utcz.com/z/267430.html

回到顶部