有道无术,术尚可求,有术无道,止于术。
本系列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;
// 查询 @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
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 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);