Skip to content

Commit 0657136

Browse files
committed
Support for Jackson's findModules and autodetection of JSR-310 and Joda-Time support
Issue: SPR-11040
1 parent 8592180 commit 0657136

File tree

1 file changed

+91
-30
lines changed

1 file changed

+91
-30
lines changed

spring-web/src/main/java/org/springframework/http/converter/json/Jackson2ObjectMapperFactoryBean.java

Lines changed: 91 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,12 @@
1818

1919
import java.text.DateFormat;
2020
import java.text.SimpleDateFormat;
21-
import java.util.ArrayList;
2221
import java.util.HashMap;
2322
import java.util.LinkedHashMap;
23+
import java.util.LinkedList;
2424
import java.util.List;
2525
import java.util.Map;
2626

27-
import org.springframework.beans.FatalBeanException;
28-
import org.springframework.beans.factory.FactoryBean;
29-
import org.springframework.beans.factory.InitializingBean;
30-
import org.springframework.util.Assert;
31-
3227
import com.fasterxml.jackson.annotation.JsonInclude;
3328
import com.fasterxml.jackson.core.JsonGenerator;
3429
import com.fasterxml.jackson.core.JsonParser;
@@ -42,6 +37,14 @@
4237
import com.fasterxml.jackson.databind.SerializationFeature;
4338
import com.fasterxml.jackson.databind.module.SimpleModule;
4439

40+
import org.springframework.beans.BeanUtils;
41+
import org.springframework.beans.FatalBeanException;
42+
import org.springframework.beans.factory.BeanClassLoaderAware;
43+
import org.springframework.beans.factory.FactoryBean;
44+
import org.springframework.beans.factory.InitializingBean;
45+
import org.springframework.util.Assert;
46+
import org.springframework.util.ClassUtils;
47+
4548
/**
4649
* A {@link FactoryBean} for creating a Jackson 2.x {@link ObjectMapper} with setters
4750
* to enable or disable Jackson features from within XML configuration.
@@ -114,25 +117,30 @@
114117
* @author <a href="mailto:[email protected]">Dmitry Katsubo</a>
115118
* @author Rossen Stoyanchev
116119
* @author Brian Clozel
120+
* @author Juergen Hoeller
117121
* @since 3.2
118122
*/
119-
public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper>, InitializingBean {
123+
public class Jackson2ObjectMapperFactoryBean implements FactoryBean<ObjectMapper>, BeanClassLoaderAware, InitializingBean {
120124

121125
private ObjectMapper objectMapper;
122126

123-
private Map<Object, Boolean> features = new HashMap<Object, Boolean>();
124-
125-
private final List<Module> modules = new ArrayList<Module>();
126-
127127
private DateFormat dateFormat;
128128

129+
private JsonInclude.Include serializationInclusion;
130+
129131
private AnnotationIntrospector annotationIntrospector;
130132

131133
private final Map<Class<?>, JsonSerializer<?>> serializers = new LinkedHashMap<Class<?>, JsonSerializer<?>>();
132134

133135
private final Map<Class<?>, JsonDeserializer<?>> deserializers = new LinkedHashMap<Class<?>, JsonDeserializer<?>>();
134136

135-
private JsonInclude.Include serializationInclusion;
137+
private final Map<Object, Boolean> features = new HashMap<Object, Boolean>();
138+
139+
private final List<Module> modules = new LinkedList<Module>();
140+
141+
private boolean findModules;
142+
143+
private ClassLoader beanClassLoader;
136144

137145

138146
/**
@@ -164,13 +172,20 @@ public void setSimpleDateFormat(String format) {
164172
}
165173

166174
/**
167-
* Set the {@link AnnotationIntrospector} for both serialization and
168-
* deserialization.
175+
* Set an {@link AnnotationIntrospector} for both serialization and deserialization.
169176
*/
170177
public void setAnnotationIntrospector(AnnotationIntrospector annotationIntrospector) {
171178
this.annotationIntrospector = annotationIntrospector;
172179
}
173180

181+
/**
182+
* Set a custom inclusion strategy for serialization.
183+
* @see com.fasterxml.jackson.annotation.JsonInclude.Include
184+
*/
185+
public void setSerializationInclusion(JsonInclude.Include serializationInclusion) {
186+
this.serializationInclusion = serializationInclusion;
187+
}
188+
174189
/**
175190
* Configure custom serializers. Each serializer is registered for the type
176191
* returned by {@link JsonSerializer#handledType()}, which must not be
@@ -270,25 +285,34 @@ public void setFeaturesToDisable(Object... featuresToDisable) {
270285
}
271286

272287
/**
273-
* Sets the custom inclusion strategy for serialization.
274-
* @see com.fasterxml.jackson.annotation.JsonInclude.Include
288+
* Set whether to let Jackson find available modules via the ServiceLoader.
289+
* Requires Jackson 2.2 or higher.
290+
* <p>If this mode is not set, Spring's Jackson2ObjectMapperFactoryBean itself
291+
* will try to find the JSR-310 and Joda-Time support modules on the classpath -
292+
* provided that Java 8 and Joda-Time themselves are available, respectively.
293+
* @see com.fasterxml.jackson.databind.ObjectMapper#findModules()
275294
*/
276-
public void setSerializationInclusion(JsonInclude.Include serializationInclusion) {
277-
this.serializationInclusion = serializationInclusion;
295+
public void setFindModules(boolean findModules) {
296+
this.findModules = findModules;
278297
}
279298

280299
/**
281-
* Sets the list of modules to be registered with {@link ObjectMapper}.
282-
* @param modules the list of modules
283-
* @see com.fasterxml.jackson.databind.Module
300+
* Set the list of modules to be registered with the {@link ObjectMapper}.
284301
* @since 4.0
302+
* @see com.fasterxml.jackson.databind.Module
285303
*/
286304
public void setModules(List<Module> modules) {
287-
if(modules != null) {
305+
if (modules != null) {
288306
this.modules.addAll(modules);
289307
}
290308
}
291309

310+
@Override
311+
public void setBeanClassLoader(ClassLoader beanClassLoader) {
312+
this.beanClassLoader = beanClassLoader;
313+
}
314+
315+
292316
@Override
293317
public void afterPropertiesSet() {
294318
if (this.objectMapper == null) {
@@ -299,26 +323,33 @@ public void afterPropertiesSet() {
299323
this.objectMapper.setDateFormat(this.dateFormat);
300324
}
301325

326+
if (this.annotationIntrospector != null) {
327+
this.objectMapper.setAnnotationIntrospector(this.annotationIntrospector);
328+
}
329+
330+
if (this.serializationInclusion != null) {
331+
this.objectMapper.setSerializationInclusion(this.serializationInclusion);
332+
}
333+
302334
if (!this.serializers.isEmpty() || !this.deserializers.isEmpty()) {
303335
SimpleModule module = new SimpleModule();
304336
addSerializers(module);
305337
addDeserializers(module);
306338
this.objectMapper.registerModule(module);
307339
}
308340

309-
if (this.annotationIntrospector != null) {
310-
this.objectMapper.setAnnotationIntrospector(this.annotationIntrospector);
311-
}
312-
313341
for (Object feature : this.features.keySet()) {
314342
configureFeature(feature, this.features.get(feature));
315343
}
316344

317-
if (this.serializationInclusion != null) {
318-
this.objectMapper.setSerializationInclusion(this.serializationInclusion);
345+
if (this.findModules) {
346+
this.objectMapper.registerModules(ObjectMapper.findModules(this.beanClassLoader));
347+
}
348+
else {
349+
registerWellKnownModulesIfAvailable();
319350
}
320351

321-
if(!this.modules.isEmpty()) {
352+
if (!this.modules.isEmpty()) {
322353
this.objectMapper.registerModules(this.modules);
323354
}
324355
}
@@ -354,7 +385,37 @@ else if (feature instanceof MapperFeature) {
354385
this.objectMapper.configure((MapperFeature) feature, enabled);
355386
}
356387
else {
357-
throw new FatalBeanException("Unknown feature class " + feature.getClass().getName());
388+
throw new FatalBeanException("Unknown feature class: " + feature.getClass().getName());
389+
}
390+
}
391+
392+
@SuppressWarnings("unchecked")
393+
private void registerWellKnownModulesIfAvailable() {
394+
ClassLoader cl = this.beanClassLoader;
395+
if (cl == null) {
396+
cl = getClass().getClassLoader();
397+
}
398+
// Java 8 java.time package present?
399+
if (ClassUtils.isPresent("java.time.LocalDate", cl)) {
400+
try {
401+
Class<? extends Module> jsr310Module = (Class<? extends Module>)
402+
cl.loadClass("com.fasterxml.jackson.datatype.jsr310.JSR310Module");
403+
this.objectMapper.registerModule(BeanUtils.instantiate(jsr310Module));
404+
}
405+
catch (ClassNotFoundException ex) {
406+
// jackson-datatype-jsr310 not available
407+
}
408+
}
409+
// Joda-Time present?
410+
if (ClassUtils.isPresent("org.joda.time.LocalDate", cl)) {
411+
try {
412+
Class<? extends Module> jodaModule = (Class<? extends Module>)
413+
cl.loadClass("com.fasterxml.jackson.datatype.joda.JodaModule");
414+
this.objectMapper.registerModule(BeanUtils.instantiate(jodaModule));
415+
}
416+
catch (ClassNotFoundException ex) {
417+
// jackson-datatype-joda not available
418+
}
358419
}
359420
}
360421

0 commit comments

Comments
 (0)