Jackson 2.x 系列【23】注解内省 AnnotationIntrospector

454 阅读4分钟

有道无术,术尚可求,有术无道,止于术。

本系列Jackson 版本 2.17.0

源码地址:https://gitee.com/pearl-organization/study-jaskson-demo

1. 前言

Annotation注解是JDK 5引入的一种机制,本身只是起到了标注作用,需要使用注解处理器、反射机制在运行时添加对应注解的处理逻辑。 例如Spring中包含了大量的注解,在运行时,Spring容器通过的反射机制获取类的注解信息,并根据这些信息创建和配置Bean对象。

Jackson中也包含了很多注解,用于配置序列化/反序列化行为,那么这些注解是如何生效的呢?

2. AnnotationIntrospector

AnnotationIntrospector注解内省抽象类,由jackson-databind模块提供,定义了一系列基于注解的内省API,用于检查类的注解并据此进行配置。

继承结构如下:

image.png

Introspector翻译过来是内省的意思,在计算机领域,内省指的是计算机程序在运行时检查对象类型的能力,通常也可以称作运行时类型检查,是一种检查对象以了解其属性、方法和事件等信息的机制。

AnnotationIntrospector定义了很多抽象方法,常用的有:

  • findDeserializer(Annotated a): 查找与注解关联的反序列化器
  • findSerializer(Annotated a):查找与注解关联的序列化器
  • findNullSerializer(Annotated a):查找处理null值的序列化器
  • hasIgnoreMarker(AnnotatedMember m):检查一个成员(字段或方法)是否标记为忽略
  • findFormat(Annotated memberOrClass):查询JsonFormat注解

例如,当前对象属性包含了一个@JsonProperty注解,username会被序列化为name字段:

    @JsonProperty("name")
    String username;

在序列化的过程中,需要获取对象的属性名称,AnnotationIntrospector中声明了相关方法:

	// 序列化是查找属性名称
    public PropertyName findNameForSerialization(Annotated a) {
        return null;
    }

在执行过程中,调用到实现类JacksonAnnotationIntrospector的实现方法,会检查是否包含@JsonGetter@JsonProperty注解,优先使用注解上的配置作为属性名称:

    public PropertyName findNameForSerialization(Annotated a) {
        boolean useDefault = false;
        // 查询 @JsonGetter
        JsonGetter jg = (JsonGetter)this._findAnnotation(a, JsonGetter.class);
        if (jg != null) {
            String s = jg.value();
            if (!s.isEmpty()) {
                return PropertyName.construct(s);
            }
            useDefault = true;
        }
		// 查询 @JsonProperty 
        JsonProperty pann = (JsonProperty)this._findAnnotation(a, JsonProperty.class);
        if (pann != null) {
        	// 存在注解时
            String ns = pann.namespace();
            if (ns != null && ns.isEmpty()) {
                ns = null;
            }
            // 
            return PropertyName.construct(pann.value(), ns);
        } else {
            return !useDefault && !this._hasOneOf(a, ANNOTATIONS_TO_INFER_SER) ? null : PropertyName.USE_DEFAULT;
        }
    }

3. JacksonAnnotationIntrospector

JacksonAnnotationIntrospectorAnnotationIntrospector的默认标准实现,用于分析处理Jackson中提供的标准注解,ObjectMapper中可以看到初始化逻辑:

public class ObjectMapper extends ObjectCodec implements Versioned, Serializable {
    
    protected static final AnnotationIntrospector DEFAULT_ANNOTATION_INTROSPECTOR =
     new JacksonAnnotationIntrospector();
}

4. AnnotationIntrospectorPair

AnnotationIntrospectorPair是一个Helper类,内部维护了两个内省器,一个首要一个辅助。

public class AnnotationIntrospectorPair extends AnnotationIntrospector implements Serializable {

    protected final AnnotationIntrospector _primary;
    
    protected final AnnotationIntrospector _secondary;
	//。。。。。。。。。。
}

例如,其查询注解配置的序列化器方法中,首先在首选的内省器中查询,没有查询到则在辅助中再次查询:

    public Object findSerializer(Annotated am) {
        Object r = this._primary.findSerializer(am);
        return this._isExplicitClassOrOb(r, JsonSerializer.None.class) ? r : this._explicitClassOrOb(this._secondary.findSerializer(am), JsonSerializer.None.class);
    }

当处理自定义注解时,可以设置JacksonAnnotationIntrospector为首选,自定义的AnnotationIntrospector为辅助内省器。

5. NopAnnotationIntrospector

NopAnnotationIntrospector即没有任何操作的内省抽象类,没有实现父类的任何方法,主要用于:

  • 在不需要处理注解的场景,可以用它覆盖JacksonAnnotationIntrospector
  • 构建自定义的AnnotationIntrospector实现
public abstract class NopAnnotationIntrospector
        extends AnnotationIntrospector
        implements java.io.Serializable {
        
    private static final long serialVersionUID = 1L;
	
	// 内部实例
    public final static NopAnnotationIntrospector instance = new NopAnnotationIntrospector() {
        private static final long serialVersionUID = 1L;

        @Override
        public Version version() {
            return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION;
        }
    };

    @Override
    public Version version() {
        return Version.unknownVersion();
    }
}

6. 案例演示

6.1 关闭注解功能

在不需要处理注解的场景,可以关闭注解内省功能,提升性能(极少场景)。

例如,下面的POJO类:

public class IntrospectorVO {

    @JsonIgnore
    public String name;

    public Long id;

    public IntrospectorVO(String name, Long id) {
        this.name = name;
        this.id = id;
    }
}

序列化是name字段将被忽略:

{"id":1767798780627279333}

使用NopAnnotationIntrospector替换默认的JacksonAnnotationIntrospector,所有的注解将不生效:

        IntrospectorVO vo=new IntrospectorVO("阿坤",1767798780627279333L);
        ObjectMapper objectMapper = new ObjectMapper();
        
        objectMapper.setAnnotationIntrospector(NopAnnotationIntrospector.nopInstance());
        
        String jsonStr = objectMapper.writeValueAsString(vo);
        System.out.println(jsonStr);

6.2 覆盖默认

可以继承JacksonAnnotationIntrospector,重写方法修改默认处理逻辑。

例如重写判断属性是否被忽略的_isIgnorable方法,返回false,那么所有的属性都不会被忽略:

public class CustomJacksonAnnotationIntrospector extends JacksonAnnotationIntrospector {

    @Override
    protected boolean _isIgnorable(Annotated a) {
        System.out.println("_isIgnorable");
        return false;
    }
}

测试代码:

        IntrospectorVO vo=new IntrospectorVO("阿坤",1767798780627279333L);
        ObjectMapper objectMapper = new ObjectMapper();
        // objectMapper.setAnnotationIntrospector(NopAnnotationIntrospector.nopInstance()); // 关闭注解
        objectMapper.setAnnotationIntrospector(new CustomJacksonAnnotationIntrospector());
        String jsonStr = objectMapper.writeValueAsString(vo);
        System.out.println(jsonStr);

6.3 自定义注解

Jackson 2.x 系列【22】自定义序列化/反序列化器中,有演示过使用自定义注解指定序列化器:

@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = ListToStringJsonSerializer.class)
@JsonDeserialize(using = ListToStringJsonDeserializer.class)
public @interface JsonListToCommaSplitString {

}

除此之外,还可以通过自定义AnnotationIntrospector重写查询序列化/反序列化器方法。

例如上述注解改为:

@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
public @interface JsonListToCommaSplitString {

}

自定义AnnotationIntrospector

public class MyAnnotationIntrospector extends NopAnnotationIntrospector {
    private static final long serialVersionUID = 1L;

    public static final MyAnnotationIntrospector instance = new MyAnnotationIntrospector();


    @Override
    public Object findSerializer(Annotated a) {
        JsonListToCommaSplitString ann = (JsonListToCommaSplitString) this._findAnnotation(a, JsonListToCommaSplitString.class);
        if (ann != null) {
            return ListToStringJsonSerializer.instance;
        }
        return null;
    }

    @Override
    public Object findDeserializer(Annotated a) {
        JsonListToCommaSplitString ann = (JsonListToCommaSplitString) this._findAnnotation(a, JsonListToCommaSplitString.class);
        if (ann != null) {
            return ListToStringJsonDeserializer.instance;
        }
        return null;
    }
}

配置:

        IntrospectorVO vo = new IntrospectorVO("阿坤", 1767798780627279333L);
        ObjectMapper objectMapper = new ObjectMapper();
        // objectMapper.setAnnotationIntrospector(NopAnnotationIntrospector.nopInstance()); // 关闭注解
        //objectMapper.setAnnotationIntrospector(new CustomJacksonAnnotationIntrospector());
        objectMapper.setAnnotationIntrospector(AnnotationIntrospector.pair(
                objectMapper.getSerializationConfig().getAnnotationIntrospector(), // jackson 原有的
                MyAnnotationIntrospector.instance // 自定义的作为辅助方案
        ));
        String jsonStr = objectMapper.writeValueAsString(vo);
        System.out.println(jsonStr);