有道无术,术尚可求,有术无道,止于术。
本系列Jackson 版本 2.17.0
源码地址:https://gitee.com/pearl-organization/study-jaskson-demo
文章目录
- 1. 前言
- 2. AnnotationIntrospector
- 3. JacksonAnnotationIntrospector
- 4. AnnotationIntrospectorPair
- 5. NopAnnotationIntrospector
- 6. 案例演示
- 6.1 关闭注解功能
- 6.2 覆盖默认
- 6.3 自定义注解
1. 前言
Annotation
注解是JDK 5
引入的一种机制,本身只是起到了标注作用,需要使用注解处理器、反射机制在运行时添加对应注解的处理逻辑。 例如Spring
中包含了大量的注解,在运行时,Spring
容器通过的反射机制获取类的注解信息,并根据这些信息创建和配置Bean
对象。
在Jackson
中也包含了很多注解,用于配置序列化/反序列化行为,那么这些注解是如何生效的呢?
2. AnnotationIntrospector
AnnotationIntrospector
注解内省抽象类,由jackson-databind
模块提供,定义了一系列基于注解的内省API
,用于检查类的注解并据此进行配置。
继承结构如下:
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;// 查询 @JsonGetterJsonGetter 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
JacksonAnnotationIntrospector
是AnnotationIntrospector
的默认标准实现,用于分析处理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 NopAnnotationIntrospectorextends AnnotationIntrospectorimplements java.io.Serializable {private static final long serialVersionUID = 1L;// 内部实例public final static NopAnnotationIntrospector instance = new NopAnnotationIntrospector() {private static final long serialVersionUID = 1L;@Overridepublic Version version() {return com.fasterxml.jackson.databind.cfg.PackageVersion.VERSION;}};@Overridepublic Version version() {return Version.unknownVersion();}
}
6. 案例演示
6.1 关闭注解功能
在不需要处理注解的场景,可以关闭注解内省功能,提升性能(极少场景)。
例如,下面的POJO
类:
public class IntrospectorVO {@JsonIgnorepublic 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 {@Overrideprotected 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();@Overridepublic Object findSerializer(Annotated a) {JsonListToCommaSplitString ann = (JsonListToCommaSplitString) this._findAnnotation(a, JsonListToCommaSplitString.class);if (ann != null) {return ListToStringJsonSerializer.instance;}return null;}@Overridepublic 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);