如果我们在Spring Boot应用中手动定义并注入了一个ObjectMapper
Bean,那么这个自定义的ObjectMapper
实例会替换掉Spring Boot默认配置的ObjectMapper
。当Spring容器中存在多个同类型的Bean时,默认情况下最后一个创建的Bean将作为首选Bean(如果未明确指定@Primary
注解),因此我们的自定义ObjectMapper
将会被所有依赖于ObjectMapper
的地方使用。
例如:
java
@Configuration
public class ObjectMapperConfig {
@Bean
public ObjectMapper customObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
// 添加自定义配置...
return objectMapper;
}
}
上述代码定义了一个自定义的ObjectMapper
Bean,并将其注册到了Spring容器中。这样一来,在整个应用中需要ObjectMapper
的地方,包括HTTP请求和响应的JSON转换等场景,都会使用到这个自定义配置的ObjectMapper
,而非Spring Boot默认提供的那个。
因此,如果我们只想修改Spring Boot默认ObjectMapper
的一些配置,而不是完全替换掉它,使用Jackson2ObjectMapperBuilderCustomizer
接口是一个更好的选择。通过实现这个接口并注册一个定制器Bean,我们可以对默认的ObjectMapper
进行扩展和修改,而不会覆盖其他默认配置。
下面是一个例子:
java
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JacksonConfig {
@Bean
public Jackson2ObjectMapperBuilderCustomizer jacksonCustomizer() {
return builder -> {
// 修改日期格式化
builder.dateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
// 关闭未知属性导致反序列化失败
builder.featuresToDisable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
// 其他自定义配置...
};
}
}
这样,我们的配置将应用于所有的ObjectMapper
实例,包括那些由Spring Boot自动配置创建的实例。这意味着在HTTP请求响应处理、消息转换等任何使用到ObjectMapper
的地方,都会采用我们自定义的配置。
另外,Jackson2ObjectMapperBuilderCustomizer
接口并不能配置空值序列化操作,因此我们可以这样: (该方法无效)
java
// 该方式不会完全替换Springboot默认的ObjectMapper,并且可以设置空值序列化器
@Bean
@Primary
@ConditionalOnMissingBean(ObjectMapper.class)
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
builder.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
builder.modules(getJavaLongSimpleModule(), getJavaTimeSimpleModule());
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {
@Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString("");
}
});
return objectMapper;
}
该方式不会完全替换Springboot默认的ObjectMapper,并且可以设置空值序列化器。
注:JsonSerializer
无法在序列化时对空值操作,因为其serialize
方法接收到的被序列化对象永远不为null
java
/**
* Abstract class that defines API used by {@link ObjectMapper} (and
* other chained {@link JsonSerializer}s too) to serialize Objects of
* arbitrary types into JSON, using provided {@link JsonGenerator}.
* {@link com.fasterxml.jackson.databind.ser.std.StdSerializer} instead
* of this class, since it will implement many of optional
* methods of this class.
*<p>
* NOTE: various <code>serialize</code> methods are never (to be) called
* with null values -- caller <b>must</b> handle null values, usually
* by calling {@link SerializerProvider#findNullValueSerializer} to obtain
* serializer to use.
* This also means that custom serializers cannot be directly used to change
* the output to produce when serializing null values.
*<p>
* If serializer is an aggregate one -- meaning it delegates handling of some
* of its contents by using other serializer(s) -- it typically also needs
* to implement {@link com.fasterxml.jackson.databind.ser.ResolvableSerializer},
* which can locate secondary serializers needed. This is important to allow dynamic
* overrides of serializers; separate call interface is needed to separate
* resolution of secondary serializers (which may have cyclic link back
* to serializer itself, directly or indirectly).
*<p>
* In addition, to support per-property annotations (to configure aspects
* of serialization on per-property basis), serializers may want
* to implement
* {@link com.fasterxml.jackson.databind.ser.ContextualSerializer},
* which allows specialization of serializers: call to
* {@link com.fasterxml.jackson.databind.ser.ContextualSerializer#createContextual}
* is passed information on property, and can create a newly configured
* serializer for handling that particular property.
*<p>
* If both
* {@link com.fasterxml.jackson.databind.ser.ResolvableSerializer} and
* {@link com.fasterxml.jackson.databind.ser.ContextualSerializer}
* are implemented, resolution of serializers occurs before
* contextualization.
*/
public abstract class JsonSerializer<T> implements JsonFormatVisitable // since 2.1
{
/**
* Method that can be called to ask implementation to serialize
* values of type this serializer handles.
*
* @param value Value to serialize; can <b>not</b> be null.
* @param gen Generator used to output resulting Json content
* @param serializers Provider that can be used to get serializers for
* serializing Objects value contains, if any.
*/
public abstract void serialize(T value, JsonGenerator gen, SerializerProvider serializers)
throws IOException;
}