javaspringspring-bootjackson

jackson ObjectMapper class no longer in jackson-databind?


I am updating an existing application from Spring-boot 2.x to 3.3.0 and Jackson 2.17.1. Spring Boot pulls in Spring Framework 6.1.8.

There seems to be a class from jackson-databind that has disappeared or been moved. I hope that someone can point out what happened to this class, because I can find no clear details about this online.

Details: Upon startup of my Spring Boot application, the Spring Boot application fails with a BeanCreationException:

Application run failed  org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.actuate.autoconfigure.endpoint.jackson.JacksonEndpointAutoConfiguration': Lookup method resolution failed
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.checkLookupMethods(AutowiredAnnotationBeanPostProcessor.java:497)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:367)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors(AbstractAutowireCapableBeanFactory.java:1296)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1191)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:562)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:522)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:337)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:975)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:962)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:624)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:456)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:335)

This is nothing happening inside my code, this is happening INSIDE Spring Framework when it tries to instantiate this internal bean inside Spring Boot:

org.springframework.boot.actuate.autoconfigure.endpoint.jackson.JacksonEndpointAutoConfiguration

The underlying problem seems to be that this class from the jackson-databind library cannot be found:

com.fasterxml.jackson.databind.ObjectMapper

The code in the Spring Framework class imports this class directly:

package org.springframework.boot.actuate.autoconfigure.endpoint.jackson;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import org.springframework.boot.actuate.endpoint.jackson.EndpointObjectMapper;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

/**
 * {@link EnableAutoConfiguration Auto-configuration} for Endpoint Jackson support.
 *
 * @author Phillip Webb
 * @since 3.0.0
 */
@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;
    }

}

This might create a problem for anyone trying to use Jackson and Spring together. Here's a graph of the dependencies (from maven dependency:tree)

[INFO] +- org.springframework.boot:spring-boot-starter-web:jar:3.3.0:compile
[INFO] |  +- (org.springframework.boot:spring-boot-starter:jar:3.3.0:compile - version managed from 3.3.0; omitted for duplicate)
[INFO] |  +- org.springframework.boot:spring-boot-starter-json:jar:3.3.0:compile (version managed from 3.3.0)
[INFO] |  |  +- (org.springframework.boot:spring-boot-starter:jar:3.3.0:compile - version managed from 3.3.0; omitted for duplicate)
[INFO] |  |  +- (org.springframework:spring-web:jar:6.1.8:compile - version managed from 6.1.8; omitted for duplicate)
[INFO] |  |  +- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:jar:2.17.1:compile (version managed from 2.17.1)
[INFO] |  |  |  \- (com.fasterxml.jackson.core:jackson-core:jar:2.17.1:compile - version managed from 2.17.1; omitted for duplicate)
[INFO] |  |  +- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar:2.17.1:compile (version managed from 2.17.1; scope not updated to compile)
[INFO] |  |  |  +- com.fasterxml.jackson.core:jackson-annotations:jar:2.17.1:compile (version managed from 2.17.1)
[INFO] |  |  |  \- (com.fasterxml.jackson.core:jackson-core:jar:2.17.1:compile - version managed from 2.17.1; omitted for duplicate)
[INFO] |  |  \- com.fasterxml.jackson.module:jackson-module-parameter-names:jar:2.17.1:compile (version managed from 2.17.1)
[INFO] |  |     \- (com.fasterxml.jackson.core:jackson-core:jar:2.17.1:compile - version managed from 2.17.1; omitted for duplicate)

Solution

  • I think that if you add a dependency on jackson-databind it should work:

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>
    

    But it is not immediately clear to me why you should need to, if you are not directly using it anywhere in your code. If it is only Spring Boot that uses it, they should declare the dependency, but it may be so common for Spring Boot projects that use Jackson to have a dependency on jackson-databind that they have overlooked it.