在 Spring Boot 中正确注册 Jackson Module

本文撰写时的 Spring 版本: spring-boot 2.0.5.RELEASE

当我们在进行 Spring Boot 开发时, REST 接口的默认返回类型是 json , 使用的序列化库为 jackson .

Spring Boot 内部使用的 ObjectMapper 是在 MappingJackson2HttpMessageConverter 里被配置好的. 于是问题就来了, 我们想要使用更多的 Jackson Module 怎么办.

默认被配置的 Jackson ModuleJdk8Module , JavaTimeModule , JodaModule , KotlinModule . 这些 Module 是在哪里被注册的呢, 通过搜索大法, 我们发现是在这里 org.springframework.http.converter.json.Jackson2ObjectMapperBuilder.configure(ObjectMapper objectMapper)

Assert.notNull(objectMapper, "ObjectMapper must not be null");

if (this.findModulesViaServiceLoader) {
    objectMapper.registerModules(ObjectMapper.findModules(this.moduleClassLoader));
}
else if (this.findWellKnownModules) {
    registerWellKnownModulesIfAvailable(objectMapper);
}

...

通过 IDEAfind usage 功能我们可以发现, findModulesViaServiceLoader 这个字段永远是 false , 根本没有方法会调用他的 setter . 更加不要提 JacksonProperties 这个设置类了, 所以这段程序永远都会走到 else if 分支.

而所谓的 WellKnownModules 就是内定的那几个 Module.

我们假设 jackson-datatype-hibernate5 是我们想要注册的 Module.

我们查看他的 jar 包会发现 services 目录里面有一个文件叫做 com.fasterxml.jackson.databind.Module . ObjectMapper.findModules(this.moduleClassLoader) 这个方法就是靠这个文件来判断 classpath 中哪些包是他的 Module.

但是因为 findModulesViaServiceLoader 永远是 false , 所以并非是只要加入依赖就可以实现 Module 的自动注册了.

KotlinModule 只要被加入依赖就可以自动注册只是因为他是内定的几个模块之一.

所以简单的加入依赖是没有作用的, 我们必须要用代码来让 Spring Boot 知道我们想要使用这个 Module.

于是我们在谷歌上搜索 spring boot jackson hibernate , 结果搜到的文章都是教人直接替换 MappingJackson2HttpMessageConverter , 可真是一个小机灵鬼.

而一旦替换这个类, 就要把它原本的代码再写一遍, Spring 版本升级之后可能还会挂掉, 所以正确做法一定不是这样.

根据 Spring Boot 标准命名法, 自动配置类的类名后缀为 AutoConfiguration , 于是我们找到了自动配置 Jackson 的地方 org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration

然后我们看到在这里 JacksonAutoConfiguration.Jackson2ObjectMapperBuilderCustomizerConfiguration.StandardJack2ObjectMapperBuilderCustomizer.configreModules(Jackson2ObjectMapperBuilder builder)

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

applicationContext 中的所有 Module 实现类被自动注册了!

所以实际上我们只需要有一个 Bean 就可以实现 Module 的自动注册了.

@Configuration
open class JacksonHibernate5Configuration {
    @Bean
    open fun dataTypeHibernateModule() = Hibernate5Module()
}

就这么简单么? 对, 就是这么简单(这么简单还绕了那么大一圈, 真的菜)!


原文:在 Spring Boot 中正确注册 Jackson Module · HonKit