乐趣区

关于java: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 标准

退出移动版