-
Notifications
You must be signed in to change notification settings - Fork 2k
Design for Enhance the ObjectMapper to support Spring Boot's pattern to enable autoconfiguration
This design is to make our current ObjectMapper
could follow Spring Boot's configuration pattern.
Users now use MessageConverters
with the default ObjectMapper which was newed by us, and this ObjectMapper doesn't support Spring Boot's configuration.
@Bean
@ConditionalOnMissingBean
public EventHubsMessageConverter eventHubsMessageConverter() {
return new EventHubsMessageConverter();
}
/**
* Construct the message converter with default {@code ObjectMapper}.
*/
public EventHubsMessageConverter() {
this(OBJECT_MAPPER);
}
public final class ObjectMapperHolder {
private ObjectMapperHolder() {
}
public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
}
So this design is to enhance the ObjectMapper to let users also can work with Spring Boot's pattern, such as:
- Configure properties
- Customize
Jackson2ObjectMapperBuilderCustomizer
- Override
ObjectMapper
/Jackson2ObjectMapperBuilder
Spring Boot uses the default ObjectMapper
created by JacksonObjectMapperConfiguration in JacksonAutoConfiguration, the ObjectMapper
will be created if there is no other ObjectMapper
in the ApplicationContext.
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Jackson2ObjectMapperBuilder.class)
static class JacksonObjectMapperConfiguration {
@Bean
@Primary
@ConditionalOnMissingBean
ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
return builder.createXmlMapper(false).build();
}
}
And next step confirmed that JacksonAutoConfiguration
was packaged in maven spring-boot-autoconfigure
. which already exists in spring-cloud-azure-autoconfigure .
For Spring-Boot 3.0.0 RC2 actuator endpoints:
Use the ObjectMapper
builded by Jackson2ObjectMapperBuilder
in JacksonEndpointAutoConfiguration.
@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter(JacksonAutoConfiguration.class)
public class JacksonEndpointAutoConfiguration {
@Bean
@ConditionalOnProperty(name = "management.endpoints.jackson.isolated-object-mapper", matchIfMissing = true)
@ConditionalOnClass({ ObjectMapper.class, Jackson2ObjectMapperBuilder.class })
public EndpointObjectMapper endpointObjectMapper() {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json()
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,
SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS)
.serializationInclusion(Include.NON_NULL).build();
return () -> objectMapper;
}
}
For spring-data-commons:
Use the SpringBoot's default ObjectMapper
in SpringDataWebConfiguration
ObjectMapper mapper = context.getBeanProvider(ObjectMapper.class).getIfUnique(ObjectMapper::new);
For AWS: Same in S3AutoConfiguration.
@ConditionalOnMissingBean
@Bean
S3ObjectConverter s3ObjectConverter(Optional<ObjectMapper> objectMapper) {
return new Jackson2JsonS3ObjectConverter(objectMapper.orElseGet(ObjectMapper::new));
}
For GCP: Same in PubSubJsonPayloadApplication
@Bean
public JacksonPubSubMessageConverter jacksonPubSubMessageConverter(ObjectMapper objectMapper) {
return new JacksonPubSubMessageConverter(objectMapper);
}
For Alibaba:
Seems doesn't use SpringBoot's default ObjectMapper
, for example: SentinelAutoConfiguration
@ConditionalOnClass(ObjectMapper.class)
@Configuration(proxyBeanMethods = false)
protected static class SentinelConverterConfiguration {
@Configuration(proxyBeanMethods = false)
protected static class SentinelJsonConfiguration {
private ObjectMapper objectMapper = new ObjectMapper();
public SentinelJsonConfiguration() {
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
}
@Bean("sentinel-json-flow-converter")
public JsonConverter jsonFlowConverter() {
return new JsonConverter(objectMapper, FlowRule.class);
}
...
-
Consider there will be some users may not want to use
ObjectMapper
from Spring context, so we could add a property to control whether to use it:Property Name Default Value Description spring.cloud.azure.message-converter.isolated-object-mapper
true
Whether to use an isolated object mapper to serialize/deserialize message in EventHubsMessageConverter/ServiceBusMessageConverter/StorageQueueMessageConverter. -
We could use two MessageConverter beans, one is using default
ObjectMapper
from SpringBootJacksonAutoConfiguration
, another is from ObjectMapperHolder:@Bean @ConditionalOnMissingBean @ConditionalOnProperty(value = "spring.cloud.azure.message-converter.isolated-object-mapper", havingValue = "true", matchIfMissing = true) EventHubsMessageConverter defaultEventHubsMessageConverter() { return new EventHubsMessageConverter(ObjectMapperHolder.OBJECT_MAPPER); } @Bean @ConditionalOnMissingBean @ConditionalOnProperty(value = "spring.cloud.azure.message-converter.isolated-object-mapper", havingValue = "false") EventHubsMessageConverter eventHubsMessageConverter(ObjectMapper objectMapper) { return new EventHubsMessageConverter(objectMapper); }
-
Add property info in
additional-spring-configuration-metadata.json
.{ "name": "spring.cloud.azure.message-converter.isolated-object-mapper", "type": "java.lang.Boolean", "defaultValue": "true", "description": "Whether to use an isolated object mapper to serialize/deserialize message in EventHubsMessageConverter/ServiceBusMessageConverter/StorageQueueMessageConverter." }
Finally, after this design, users could use spring's way to configure ObjectMapper and converter message when setting the property as false, or users can use our old ObjectMapper
with nothing to configure.
- we also consider whether to add more properties for each messageConverter, but it is relatively rare in terms of user usage scenarios and is a bit redundant in terms of design, so we won't use it at present.
- Frequently Asked Questions
- Azure Identity Examples
- Configuration
- Performance Tuning
- Android Support
- Unit Testing
- Test Proxy Migration
- Azure Json Migration
- New Checkstyle and Spotbugs pattern migration
- Protocol Methods
- TypeSpec-Java Quickstart