ObjectMapper,别再像个二货一样一直new了!(后续)

>>强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!

前言

这篇文章是对 https://juejin.cn/post/7086443387636678670  这篇文章的后续补充。

其文章主要介绍的是在使用ObjectMapper时,全局只需一个即可,这样会快很多,评论区有位仁兄说了:既然spring用的也是这个对象,那能不能直接注入spring实例化好的。我一直没找到这个bean。

所以,今天的内容是,在我们使用ObjectMapper时,不再自己管理实例,而是借用Spring的。

其实也很简单,SpringBoot会向容器直接注册一个名为jacksonObjectMapper的ObjectMapper对象,直接通过Autowired或者Resource拿来用即可,确实向人家标题所说,别在像一个二货一样一直new了。

那如何在非Spring管理的类中使用这个对象呢?写法有很多,下面举例子说明。

第一种办法是实现BeanFactoryAware,然后从容器中获取ObjectMapper,并且赋值给static类型变量,其他类就可以直接调用。

@Service
class SetBeanFactory : BeanFactoryAware {
    companion object {
        lateinit var objectMapper: ObjectMapper
    }
    override fun setBeanFactory(beanFactory: BeanFactory) {
        objectMapper = beanFactory.getBean("jacksonObjectMapper"as ObjectMapper
        println(objectMapper)
    }
}

第二种和第一种一样,在启动完毕后run方法会返回一个上下文对象,同样可以调用getBean获取,并保存在static类型变量。

fun main(args: Array<String>) {
    var application =
        runApplication<SpringDemoApplication>(*args) 
    var objectMapper = application.getBean("jacksonObjectMapper") as ObjectMapper
}

用法很简单,我们说下SpringBoot中的ObjectMapper是哪来的。

我们从SpringBoot使用他时候说起,可以追随到AbstractJackson2HttpMessageConverter,其下的writeInternal方法用来向客户端响应流中输出被ObjectMapper转换后到JSON字符串。

Spring是这样做的,他首先通过createGenerator构造一个JsonGenerator对象,用来指定把结果写到哪里,并且指定要使用的编码,通常为utf-8,下面这个例子是写到一个FileOutputStream对象中,而Spring就是把这个对象替换为客户端的OutputStream,详细可自己查看AbstractJackson2HttpMessageConverter源码。

fun main() {
    val objectMapper = ObjectMapper()
    val jsonGenerator =
        objectMapper.factory.createGenerator(FileOutputStream("/home/HouXinLin/www/1.txt"), JsonEncoding.UTF8)
    objectMapper.writeValue(jsonGenerator, mutableMapOf(1 to "1"))
}

而在源码中,他使用的ObjectMapper对象是由构造方法中传递过来的,所以我们要找到是谁调用了AbstractJackson2HttpMessageConverter,但是由于他是个抽象类,所以我们得找到具体使用到的子类,那就是MappingJackson2HttpMessageConverter。

public MappingJackson2HttpMessageConverter() {
   this(Jackson2ObjectMapperBuilder.json().build());
}

到这里其实就复杂了,SpringBoot的自动配置起到了至关重要的作用,也容易把你迷惑,你在这里打上断点,会发现,这里被调用了好几次,而只有一次是真的,其他我没详细看调用流程,不太清楚SpringBoot为什么会这么做,而这几次中,只有SpringBoot自动配置下生成的才是最终要使用的MappingJackson2HttpMessageConverter,但这是默认情况。

SpringBoot会在自动配置下向容器导入ObjectMapper,具体类是JacksonAutoConfiguration,而我们上面通过Autowired注入的对象就是这里返回的。

@Bean
@Primary
@ConditionalOnMissingBean
ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
   ObjectMapper build = builder.createXmlMapper(false).build();
   return build;
}

但他标有@ConditionalOnMissingBean注解,所以SpringBoot内部会检测如果容器中已经有了一个ObjectMapper,那么SpringBoot将不会进行new,而是用现成的。

而还有一个个配置类JacksonHttpMessageConvertersConfiguration,用于向容器导入MappingJackson2HttpMessageConverter。

@Bean
@ConditionalOnMissingBean(value = MappingJackson2HttpMessageConverter.class,
      ignoredType 
= {
            "org.springframework.hateoas.server.mvc.TypeConstrainedMappingJackson2HttpMessageConverter",
            "org.springframework.data.rest.webmvc.alps.AlpsJsonHttpMessageConverter" })
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
   MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter(objectMapper);
   return mappingJackson2HttpMessageConverter;
}

注意两点,一是标有@ConditionalOnMissingBean注解,也同上,二是方法参数,这个时候SpringBoot会在内部检测容器中是否有ObjectMapper,显然这里是有的,所以就把他传递过来。

除了ObjectMapper,其实还有很多可以借助SpringBoot的,我们不必在自己创建,以后文章会说明,但确实SpringBoot有些庞大了,就算做一个小项目,容器中都会至少保存150个对象,显然后些浪费,但最近相信各大文章也已经看到了,Google开源了一个类似框架Guice,具体还没用过,等以后再说吧。

- END -


原文始发于微信公众号(十四个字节):ObjectMapper,别再像个二货一样一直new了!(后续)