jackson可通过@formatteddate注解+contextualserializer实现字段级日期格式化,支持动态读取pattern参数并复用序列化器,避免反射性能损耗。

在 Java 中,Jackson 通过 @JsonSerialize 注解 + 自定义 JsonSerializer 可以灵活控制任意类型(包括自定义类、字段、集合等)的 JSON 输出格式。若想进一步解耦和复用,可结合自定义注解实现“声明式序列化”,让业务代码更简洁、可读性更高。
定义自定义注解
先创建一个用于标记字段或类型的注解,比如 @FormattedDate,它不带逻辑,只起标识作用:
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface FormattedDate {
String pattern() default "yyyy-MM-dd HH:mm:ss";
}
这个注解支持加在字段上(如 private Date createTime;),也支持加在类上(配合 @JsonSerialize(using = ...) 全局生效)。
编写对应的 JsonSerializer
实现 JsonSerializer<date></date>(或其他目标类型),并在其中读取注解参数:
public class FormattedDateSerializer extends JsonSerializer<date> {
@Override
public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers)
throws IOException {
if (value == null) {
gen.writeNull();
return;
}
// 尝试从字段/类型上获取 @FormattedDate 的 pattern
AnnotatedMember member = serializers.getActiveView() != null
? serializers.getAnnotationIntrospector().findAnnotation(
serializers.getActiveView(), FormattedDate.class)
: null;
// 更实用的做法:由 ContextualSerializer 提前解析注解(推荐)
// 见下一节
String pattern = "yyyy-MM-dd HH:mm:ss"; // 默认值
gen.writeString(new SimpleDateFormat(pattern).format(value));
}
}</date>
⚠️ 注意:直接在 serialize() 里反射取注解较麻烦且性能差,**推荐使用 ContextualSerializer**。
用 ContextualSerializer 动态绑定注解参数
让序列化器能感知所在字段的注解配置,关键在于实现 ContextualSerializer 接口:
- 重写
createContextual()方法,在序列化器被初始化时提取当前字段上的@FormattedDate - 把
pattern存入序列化器实例中,后续serialize()直接使用
public class FormattedDateSerializer extends JsonSerializer<date>
implements ContextualSerializer {
private final String pattern;
public FormattedDateSerializer() {
this("yyyy-MM-dd HH:mm:ss");
}
public FormattedDateSerializer(String pattern) {
this.pattern = pattern;
}
@Override
public JsonSerializer> createContextual(SerializerProvider prov, BeanProperty property) {
if (property != null) {
FormattedDate ann = property.getAnnotation(FormattedDate.class);
if (ann != null) {
return new FormattedDateSerializer(ann.pattern());
}
}
return this;
}
@Override
public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers)
throws IOException {
if (value == null) {
gen.writeNull();
return;
}
gen.writeString(new SimpleDateFormat(pattern).format(value));
}
}</date>
在字段或类上使用注解
现在就可以像这样使用了:
public class User {
private String name;
@FormattedDate(pattern = "yyyy/MM/dd")
private Date registerTime;
@FormattedDate // 用默认格式
private Date lastLogin;
}
序列化时,Jackson 会自动调用 createContextual() 拿到对应字段的 pattern,再执行格式化输出。
如果想全局对所有 Date 生效,也可在类上加 @JsonSerialize(using = FormattedDateSerializer.class),但此时无法按字段差异化配置——所以字段级注解 + ContextualSerializer 是更灵活的选择。
不复杂但容易忽略的是:必须确保 Jackson 的 ObjectMapper 启用了注解支持(默认已启用),且不要手动注册该 serializer(否则会绕过注解机制)。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











