1818
1919import java .text .DateFormat ;
2020import java .text .SimpleDateFormat ;
21- import java .util .ArrayList ;
2221import java .util .HashMap ;
2322import java .util .LinkedHashMap ;
23+ import java .util .LinkedList ;
2424import java .util .List ;
2525import 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-
3227import com .fasterxml .jackson .annotation .JsonInclude ;
3328import com .fasterxml .jackson .core .JsonGenerator ;
3429import com .fasterxml .jackson .core .JsonParser ;
4237import com .fasterxml .jackson .databind .SerializationFeature ;
4338import 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.
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