Browse Source

Automatically find and add Jackson modules

Closes gh-47485
pull/47596/head
Andy Wilkinson 2 months ago
parent
commit
52eea3d491
  1. 4
      documentation/spring-boot-docs/src/docs/antora/modules/how-to/pages/spring-mvc.adoc
  2. 1
      module/spring-boot-jackson/build.gradle
  3. 3
      module/spring-boot-jackson/src/main/java/org/springframework/boot/jackson/autoconfigure/JacksonAutoConfiguration.java
  4. 14
      module/spring-boot-jackson/src/main/java/org/springframework/boot/jackson/autoconfigure/JacksonProperties.java
  5. 22
      module/spring-boot-jackson/src/test/java/org/springframework/boot/jackson/autoconfigure/JacksonAutoConfigurationTests.java

4
documentation/spring-boot-docs/src/docs/antora/modules/how-to/pages/spring-mvc.adoc

@ -110,7 +110,9 @@ The context's javadoc:tools.jackson.databind.json.JsonMapper.Builder[] can be cu @@ -110,7 +110,9 @@ The context's javadoc:tools.jackson.databind.json.JsonMapper.Builder[] can be cu
Such customizer beans can be ordered (Boot's own customizer has an order of 0), letting additional customization be applied both before and after Boot's customization.
Any beans of type javadoc:tools.jackson.databind.JacksonModule[] are automatically registered with the auto-configured javadoc:tools.jackson.databind.json.JsonMapper.Builder[] and are applied to any javadoc:tools.jackson.databind.json.JsonMapper[] instances that it creates.
This provides a global mechanism for contributing custom modules when you add new features to your application.
This provides an application-wide mechanism for contributing custom modules when you add new features to your application.
Any modules that participate in the Java `ServiceLoader` mechanism are, by default, found and added to the auto-configured javadoc:tools.jackson.databind.json.JsonMapper.Builder[]. To disable this behavior, set configprop:spring.jackson.find-and-add-modules[] to `false`.
If you want to replace the default javadoc:tools.jackson.databind.json.JsonMapper[] completely, either define a javadoc:org.springframework.context.annotation.Bean[format=annotation] of that type or, if you prefer the builder-based approach, define a javadoc:tools.jackson.databind.json.JsonMapper.Builder[] javadoc:org.springframework.context.annotation.Bean[format=annotation].
When defining an javadoc:tools.jackson.databind.json.JsonMapper[] bean, marking it as javadoc:org.springframework.context.annotation.Primary[format=annotation] is recommended as the auto-configuration's javadoc:tools.jackson.databind.json.JsonMapper[] that it will replace is javadoc:org.springframework.context.annotation.Primary[format=annotation].

1
module/spring-boot-jackson/build.gradle

@ -37,6 +37,7 @@ dependencies { @@ -37,6 +37,7 @@ dependencies {
testImplementation(project(":core:spring-boot-test"))
testImplementation(project(":test-support:spring-boot-test-support"))
testImplementation("tools.jackson.module:jackson-module-kotlin")
testRuntimeOnly("ch.qos.logback:logback-classic")
}

3
module/spring-boot-jackson/src/main/java/org/springframework/boot/jackson/autoconfigure/JacksonAutoConfiguration.java

@ -170,6 +170,9 @@ public final class JacksonAutoConfiguration { @@ -170,6 +170,9 @@ public final class JacksonAutoConfiguration {
.disable(DateTimeFeature.WRITE_DATES_AS_TIMESTAMPS,
DateTimeFeature.WRITE_DURATIONS_AS_TIMESTAMPS);
}
if (this.jacksonProperties.isFindAndAddModules()) {
builder.findAndAddModules(getClass().getClassLoader());
}
if (this.jacksonProperties.getDefaultPropertyInclusion() != null) {
builder.changeDefaultPropertyInclusion((handler) -> handler
.withValueInclusion(this.jacksonProperties.getDefaultPropertyInclusion()));

14
module/spring-boot-jackson/src/main/java/org/springframework/boot/jackson/autoconfigure/JacksonProperties.java

@ -115,6 +115,12 @@ public class JacksonProperties { @@ -115,6 +115,12 @@ public class JacksonProperties {
*/
private boolean useJackson2Defaults = false;
/**
* Whether to find and add modules to the auto-configured JsonMapper.Builder using
* MapperBuilder.findAndAddModules(ClassLoader).
*/
private boolean findAndAddModules = true;
private final Datatype datatype = new Datatype();
private final Json json = new Json();
@ -199,6 +205,14 @@ public class JacksonProperties { @@ -199,6 +205,14 @@ public class JacksonProperties {
this.useJackson2Defaults = useJackson2Defaults;
}
public boolean isFindAndAddModules() {
return this.findAndAddModules;
}
public void setFindAndAddModules(boolean findModules) {
this.findAndAddModules = findModules;
}
public Datatype getDatatype() {
return this.datatype;
}

22
module/spring-boot-jackson/src/test/java/org/springframework/boot/jackson/autoconfigure/JacksonAutoConfigurationTests.java

@ -52,6 +52,7 @@ import tools.jackson.databind.json.JsonMapper; @@ -52,6 +52,7 @@ import tools.jackson.databind.json.JsonMapper;
import tools.jackson.databind.json.JsonMapper.Builder;
import tools.jackson.databind.module.SimpleModule;
import tools.jackson.databind.util.StdDateFormat;
import tools.jackson.module.kotlin.KotlinModule;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
@ -269,11 +270,11 @@ class JacksonAutoConfigurationTests { @@ -269,11 +270,11 @@ class JacksonAutoConfigurationTests {
@Test
void disableMapperFeature() {
this.contextRunner.withPropertyValues("spring.jackson.mapper.use_annotations:false").run((context) -> {
this.contextRunner.withPropertyValues("spring.jackson.mapper.infer-property-mutators:false").run((context) -> {
JsonMapper mapper = context.getBean(JsonMapper.class);
assertThat(MapperFeature.USE_ANNOTATIONS.enabledByDefault()).isTrue();
assertThat(mapper.deserializationConfig().isEnabled(MapperFeature.USE_ANNOTATIONS)).isFalse();
assertThat(mapper.serializationConfig().isEnabled(MapperFeature.USE_ANNOTATIONS)).isFalse();
assertThat(MapperFeature.INFER_PROPERTY_MUTATORS.enabledByDefault()).isTrue();
assertThat(mapper.deserializationConfig().isEnabled(MapperFeature.INFER_PROPERTY_MUTATORS)).isFalse();
assertThat(mapper.serializationConfig().isEnabled(MapperFeature.INFER_PROPERTY_MUTATORS)).isFalse();
});
}
@ -591,6 +592,19 @@ class JacksonAutoConfigurationTests { @@ -591,6 +592,19 @@ class JacksonAutoConfigurationTests {
});
}
@Test
void shouldFindAndAddModulesByDefault() {
this.contextRunner.run((context) -> assertThat(context.getBean(JsonMapper.class).getRegisteredModules())
.hasAtLeastOneElementOfType(KotlinModule.class));
}
@Test
void shouldNotFindAndAddModulesWhenDisabled() {
this.contextRunner.withPropertyValues("spring.jackson.find-and-add-modules=false")
.run((context) -> assertThat(context.getBean(JsonMapper.class).getRegisteredModules())
.doesNotHaveAnyElementsOfTypes(KotlinModule.class));
}
static class MyDateFormat extends SimpleDateFormat {
MyDateFormat() {

Loading…
Cancel
Save