2

scenes to be used

Jackson mixins are used to achieve the effect of adding Jackson's annotations to the target class when the target class is declared and defined. Especially when we use third-party class libraries, this mechanism will be particularly useful.

Next we show some examples of actual usage scenarios:

Take spring security as an example

Suppose we want to deserialize this class:

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)

We cannot deserialize these two classes because this is a typical immutable class and all property fields are initialized in the constructor. And jackson's default deserialization strategy requires a no-argument constructor and provides field introspection (Note 1) function. If we want to change the deserialization strategy, jackson needs us to add corresponding annotations to the object.

Now let's create a mixin class to solve this problem! In our mixin, we will declare the annotations we need for deserialization:

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) {
    }
}

We created a mixin class and declared the typeInfo information and constructor information to be used for it

After that, we need to tell Jackson to use our mixin. To do this, we need to implement our own extender through jackson's Module extension mechanism and tell jackson to use our 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);
    }
}

Finish!

In this code, taken from org.srpingframework.security:spring-security-web:5.1.5.RELEASE

this example, the designers of spring security faced a typical scalability problem:
Spring security has completed the design of DefaultCsrfToken long before access to jackson, but the initial writing method is not well compatible with jackson's deserialization strategy.

Fortunately, jackson provides a mixins mechanism, which can support plug-in serialization/deserialization policy declarations, thus avoiding intrusive changes to the core data structure of spring security. have to say, the design is really good!

Summarize

The use of the mixins mechanism is as described above. From the user's point of view, it is very simple and completely declarative. And more importantly, doesn't require any invasive changes to the original data structure design , which is so important to ensure that our code is clean enough to be easily open to other JSON libraries such as GSON.

So how are mixins actually implemented? Please listen to the next breakdown: Reserved Portal

Remark

Note 1: Introspection: introspector, the getter/setter specification for beans in java


Sivan
525 声望90 粉丝

行业分两个赛道,一个是ToC,一个是 ToB。