Search code examples
javaspring-bootjacksonjackson-databindjackson-modules

Jackson module not registered after update to Spring Boot 2


I'm as upgrading from Spring Boot 1.5.21 to 2.2.5.

I need to use MonetaryModule to deserialize rest calls, and depending on Spring's ObjectMapper.

I have defined such a bean for this module in a some @Configuration class (MonetaryModule is extending Module):

@Bean
public MonetaryModule monetaryModule() {
    return new MonetaryModule();
}

I can see in /beans endpoint it was created. However, it is not actually loaded to ObjectMapper. After a lot of debugging and digging around Spring's code, I came to the conclusion that there is something wrong in JacksonAutoConfiguration. it has an inner static class called JacksonObjectMapperBuilderConfiguration and in it there's a bean that creates the Jackson2ObjectMapperBuilder. in the creation process, there's a call to customize() that eventually gets to this code:

private void configureModules(Jackson2ObjectMapperBuilder builder) {
    Collection<Module> moduleBeans = getBeans(this.applicationContext, Module.class);
    builder.modulesToInstall(moduleBeans.toArray(new Module[0]));
}

This code seems responsible for loading the modules into ObjectMapper, Problem is that this Jackson2ObjectMapperBuilder is not actually created. It appears in /beans endpoint but de facto when I breakpoint there I'm not hitting the breakpoint. This explains why the module is not loaded into ObjectMapper.

Question is, why is this code not being called? and why /bean indicates the bean does exist?

Edit - Adding conditions evaluation report:

============================
CONDITIONS EVALUATION REPORT
============================


Positive matches:
-----------------

   AopAutoConfiguration matched:
      - @ConditionalOnProperty (spring.aop.auto=true) matched (OnPropertyCondition)

   AopAutoConfiguration.AspectJAutoProxyingConfiguration matched:
      - @ConditionalOnClass found required class 'org.aspectj.weaver.Advice' (OnClassCondition)

   AopAutoConfiguration.AspectJAutoProxyingConfiguration.CglibAutoProxyConfiguration matched:
      - @ConditionalOnProperty (spring.aop.proxy-target-class=true) matched (OnPropertyCondition)

   ConfigServiceBootstrapConfiguration#configServicePropertySource matched:
      - @ConditionalOnProperty (spring.cloud.config.enabled) matched (OnPropertyCondition)
      - @ConditionalOnMissingBean (types: org.springframework.cloud.config.client.ConfigServicePropertySourceLocator; SearchStrategy: all) did not find any beans (OnBeanCondition)

   ConfigServiceBootstrapConfiguration.RetryConfiguration matched:
      - @ConditionalOnClass found required classes 'org.springframework.retry.annotation.Retryable', 'org.aspectj.lang.annotation.Aspect', 'org.springframework.boot.autoconfigure.aop.AopAutoConfiguration' (OnClassCondition)
      - @ConditionalOnProperty (spring.cloud.config.fail-fast) matched (OnPropertyCondition)

   ConfigServiceBootstrapConfiguration.RetryConfiguration#configServerRetryInterceptor matched:
      - @ConditionalOnMissingBean (names: configServerRetryInterceptor; SearchStrategy: all) did not find any beans (OnBeanCondition)

   ConfigurationPropertiesRebinderAutoConfiguration matched:
      - @ConditionalOnBean (types: org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor; SearchStrategy: all) found bean 'org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor' (OnBeanCondition)

   ConfigurationPropertiesRebinderAutoConfiguration#configurationPropertiesBeans matched:
      - @ConditionalOnMissingBean (types: org.springframework.cloud.context.properties.ConfigurationPropertiesBeans; SearchStrategy: current) did not find any beans (OnBeanCondition)

   ConfigurationPropertiesRebinderAutoConfiguration#configurationPropertiesRebinder matched:
      - @ConditionalOnMissingBean (types: org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder; SearchStrategy: current) did not find any beans (OnBeanCondition)

   EncryptionBootstrapConfiguration matched:
      - @ConditionalOnClass found required class 'org.springframework.security.crypto.encrypt.TextEncryptor' (OnClassCondition)

   PropertyPlaceholderAutoConfiguration#propertySourcesPlaceholderConfigurer matched:
      - @ConditionalOnMissingBean (types: org.springframework.context.support.PropertySourcesPlaceholderConfigurer; SearchStrategy: current) did not find any beans (OnBeanCondition)


Negative matches:
-----------------

   AopAutoConfiguration.AspectJAutoProxyingConfiguration.JdkDynamicAutoProxyConfiguration:
      Did not match:
         - @ConditionalOnProperty (spring.aop.proxy-target-class=false) did not find property 'proxy-target-class' (OnPropertyCondition)

   AopAutoConfiguration.ClassProxyingConfiguration:
      Did not match:
         - @ConditionalOnMissingClass found unwanted class 'org.aspectj.weaver.Advice' (OnClassCondition)

   DiscoveryClientConfigServiceBootstrapConfiguration:
      Did not match:
         - @ConditionalOnProperty (spring.cloud.config.discovery.enabled) did not find property 'spring.cloud.config.discovery.enabled' (OnPropertyCondition)

   EncryptionBootstrapConfiguration.RsaEncryptionConfiguration:
      Did not match:
         - Keystore nor key found in Environment (EncryptionBootstrapConfiguration.KeyCondition)
      Matched:
         - @ConditionalOnClass found required class 'org.springframework.security.rsa.crypto.RsaSecretEncryptor' (OnClassCondition)

   EncryptionBootstrapConfiguration.VanillaEncryptionConfiguration:
      Did not match:
         - @ConditionalOnMissingClass found unwanted class 'org.springframework.security.rsa.crypto.RsaSecretEncryptor' (OnClassCondition)


Exclusions:
-----------

    None


Unconditional classes:
----------------------

    None

Also, attached is the output of /conditions & /beans endpoints output (Too large to paste into the post itself)

endpoints output


Solution

  • Your condition evaluation report shows the following:

            "JacksonAutoConfiguration.JacksonObjectMapperConfiguration#jacksonObjectMapper": {
              "notMatched": [
                {
                  "condition": "OnBeanCondition",
                  "message": "@ConditionalOnMissingBean (types: com.fasterxml.jackson.databind.ObjectMapper; SearchStrategy: all) found beans of type 'com.fasterxml.jackson.databind.ObjectMapper' jacksonBuilder"
                }
              ],
              "matched": []
            },
    

    This means that the Spring Boot auto-configuration condition is backing off since your application already provides an opinion on the matter: there is already an ObjectMapper bean named jacksonBuilder; it is provided by the application or some configuration class in a library you're using.

    In this case, this comes from a configuration class, as pointed out by the beans endpoint:

            "jacksonBuilder": {
              "aliases": [],
              "scope": "singleton",
              "type": "com.fasterxml.jackson.databind.ObjectMapper",
              "resource": "class path resource [com/behalf/core/authorization_domain/config/AuthorizationDomainConfiguration.class]",
              "dependencies": []
            },
    

    You should here get in touch with the maintainers of this AuthorizationDomainConfiguration and let them know that they should avoid overriding the opinion of the application on JSON (de)serialization.