2019-04-16|SwaggerUI界面model schema不生成問題

今天同事遇到一個(gè)問題,swagger-ui界面每個(gè)接口的response的model schema 都沒有生成,如下圖所示:


image.png

用的swagger版本:

    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>2.4.0</version>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>2.4.0</version>
    </dependency>

本地跑了下,發(fā)現(xiàn)啟動(dòng)就有告警信息:

18:09:33.596 [main] WARN  Exception calculating properties for model(com.xxx.xxx.mobile.client.dto.resp.CarBrandResp) -> ModelContext{type=com.xxx.xxx.mobile.client.dto.resp.CarBrandResp, isReturnType=true}. java.lang.NullPointerException

Debug了下發(fā)現(xiàn)OptimizedModelPropertiesProviderobjectMapper為空導(dǎo)致的NPE.詳細(xì)堆棧信息記錄如下:

      at springfox.documentation.schema.property.OptimizedModelPropertiesProvider.beanDescription(OptimizedModelPropertiesProvider.java:350)
      at springfox.documentation.schema.property.OptimizedModelPropertiesProvider.propertiesFor(OptimizedModelPropertiesProvider.java:117)
      at springfox.documentation.schema.property.CachingModelPropertiesProvider$1.load(CachingModelPropertiesProvider.java:56)
      at springfox.documentation.schema.property.CachingModelPropertiesProvider$1.load(CachingModelPropertiesProvider.java:54)
      at com.google.common.cache.LocalCache$LoadingValueReference.loadFuture(LocalCache.java:3527)
      at com.google.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2319)
      at com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2282)
      - locked <0x1a49> (a com.google.common.cache.LocalCache$StrongAccessWriteEntry)
      at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2197)
      at com.google.common.cache.LocalCache.get(LocalCache.java:3937)
      at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:3941)
      at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:4824)
      at springfox.documentation.schema.property.CachingModelPropertiesProvider.propertiesFor(CachingModelPropertiesProvider.java:64)
      at springfox.documentation.schema.DefaultModelProvider.properties(DefaultModelProvider.java:151)
      at springfox.documentation.schema.DefaultModelProvider.modelFor(DefaultModelProvider.java:84)
      at springfox.documentation.schema.CachingModelProvider$1.load(CachingModelProvider.java:51)
      at springfox.documentation.schema.CachingModelProvider$1.load(CachingModelProvider.java:49)
      at com.google.common.cache.LocalCache$LoadingValueReference.loadFuture(LocalCache.java:3527)
      at com.google.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2319)
      at com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2282)
      - locked <0x1a74> (a com.google.common.cache.LocalCache$StrongAccessWriteEntry)
      at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2197)
      at com.google.common.cache.LocalCache.get(LocalCache.java:3937)
      at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:3941)
      at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:4824)
      at springfox.documentation.schema.CachingModelProvider.modelFor(CachingModelProvider.java:59)
      at springfox.documentation.spring.web.scanners.ApiModelReader.read(ApiModelReader.java:67)
      at springfox.documentation.spring.web.scanners.ApiListingScanner.scan(ApiListingScanner.java:88)
      at springfox.documentation.spring.web.scanners.ApiDocumentationScanner.scan(ApiDocumentationScanner.java:69)
      at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.scanDocumentation(DocumentationPluginsBootstrapper.java:105)
      at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.onApplicationEvent(DocumentationPluginsBootstrapper.java:91)
      at springfox.documentation.spring.web.plugins.DocumentationPluginsBootstrapper.onApplicationEvent(DocumentationPluginsBootstrapper.java:53)
      at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
      at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
      at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
      at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:393)
      at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:399)
      at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:347)
      at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:883)
      at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:546)
      - locked <0x1a75> (a java.lang.Object)
      at org.springframework.cloud.context.named.NamedContextFactory.createContext(NamedContextFactory.java:116)
      at org.springframework.cloud.context.named.NamedContextFactory.getContext(NamedContextFactory.java:85)
      - locked <0x1a76> (a java.util.concurrent.ConcurrentHashMap)
      at org.springframework.cloud.context.named.NamedContextFactory.getInstance(NamedContextFactory.java:121)
      at org.springframework.cloud.netflix.feign.FeignClientFactoryBean.get(FeignClientFactoryBean.java:193)
      at org.springframework.cloud.netflix.feign.FeignClientFactoryBean.feign(FeignClientFactoryBean.java:84)
      at org.springframework.cloud.netflix.feign.FeignClientFactoryBean.getObject(FeignClientFactoryBean.java:221)
      at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:168)
      at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:103)
      - locked <0x1a77> (a java.util.concurrent.ConcurrentHashMap)
      at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1634)
      at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:254)
      at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
      at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:208)
      at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1316)
      at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1282)
      at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1101)
      at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
      at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
      at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
      at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366)
      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264)
      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
      at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
      at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
      at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
      at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
      at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:208)
      at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1138)
      at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
      at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
      at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
      at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366)
      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264)
      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
      at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
      at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
      at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
      at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
      at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
      at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)
      at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
      at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
      - locked <0x1a78> (a java.lang.Object)
      at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
      at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693)
      at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360)
      at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
      at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118)
      at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107)
      at com.xxx.xxx.mobile.MobileApplication.main(MobileApplication.java:21)

github上已有相關(guān)issue:
CachingModelPropertiesProvider - NullPointerException
【praveen12bnitt】解釋了原因:

What i found was, the NPE is coming for OptimizedModelPropertiesProvider because the objectMapper is null.
When a Feign client is added to the application, spring-cloud creates a child application context for feign beans. Check spring cloud's NamedContextFactory for details on how they create the context. Once this context is refreshed, it signals a ContextRefreshedEvent which is picked up by DocumentationPluginsBootstrapper. The problem is the main application context initialization is still not complete. RequestMappingHandlerAdapter is not even created at this time and so ObjectMapperConfigurer does not fire ObjectMapperConfigured. This is the reason why OptimizedModelPropertiesProvider has a null object mapper.
When RequestMappingHandlerAdapter is created at a later time by the main application context, all the events are fired, but its too late. DocumentationPluginsBootstrapper has already finished its model evaluation.
A possible fix is to wait for the main application's ContextRefreshedEvent, and skip the ContextRefreshedEvent published by spring-cloud. But i am not sure how to do it.

issue中解釋的還是很清楚的,結(jié)合堆棧信息:
當(dāng)引入Feign客戶端時(shí),SpringCloud創(chuàng)建了一個(gè)子應(yīng)用程序上下文FeignContext;創(chuàng)建過程中會(huì)調(diào)用refresh方法,這個(gè)方法里會(huì)派發(fā)ContextRefreshedEvent事件;而DocumentationPluginsBootstrapper會(huì)監(jiān)聽這個(gè)事件,然后開始掃描API構(gòu)建Documentation;這里的NPE正是在構(gòu)建Model的過程中報(bào)錯(cuò)的;
NPE是因?yàn)椋捍藭r(shí)主應(yīng)用程序上下文初始化仍未完成,主應(yīng)用程序上下文在配置類WebMvcAutoConfiguration中會(huì)創(chuàng)建RequestMappingHandlerAdapter實(shí)例;
配置類SpringfoxWebMvcConfiguration中會(huì)創(chuàng)建ObjectMapperConfigurer實(shí)例,這個(gè)類實(shí)現(xiàn)了BeanPostProcessor接口,會(huì)在任何bean初始化之前調(diào)用postProcessBeforeInitialization方法,這個(gè)方法內(nèi)部判斷了如果bean是RequestMappingHandlerAdapter,那么會(huì)派發(fā)一個(gè)ObjectMapperConfigured事件;
OptimizedModelPropertiesProvider監(jiān)聽了這個(gè)事件,從而設(shè)置objectMapper;

如果我們沒有使用FeignClient,那么一切還是很完美的;但是引入FeignClient之后,DocumentationPluginsBootstrapper過早的監(jiān)聽到了springcloud派發(fā)的事件;實(shí)際上應(yīng)該監(jiān)聽的是主應(yīng)用程序上下文派發(fā)的事件。

issue中給出了解決方法:讓DocumentationPluginsBootstrapper實(shí)現(xiàn)SmartLifecycle接口,這個(gè)是在所有bean加載并初始化完成之后才執(zhí)行的,其實(shí)就是讓DocumentationPluginsBootstrapper延遲執(zhí)行構(gòu)建Documentation的功能。

這個(gè)在后面的版本2.5.0中得到了解決,遂升級(jí)了版本...

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 事件和平時(shí)所用的回調(diào)思想在與GUI(JavaScript,Swing)相關(guān)的技術(shù)中非常流行。而在Web應(yīng)用程序的服...
    Java小鋪閱讀 1,148評(píng)論 0 0
  • 本篇文章是基于谷歌有關(guān)Graphic的一篇概覽文章的翻譯:http://source.android.com/de...
    lee_3do閱讀 7,478評(píng)論 2 21
  • 正文之前 終于翻譯完了,可以開始看論文了,開心啊。。。。。。 正文 Event time for a given ...
    張照博閱讀 1,683評(píng)論 2 6
  • Sonar翻譯 Sonar翻譯... 1 User Guide(用戶指南)... 2 第一章Fixing the ...
    pig_zzZ閱讀 3,098評(píng)論 0 1
  • 今天是什么日子 起床:六點(diǎn) 就寢:十一點(diǎn) 天氣:晴 心情:一般 紀(jì)念日:無 叫我起床的不是鬧鐘是夢(mèng)想 年度目標(biāo)及關(guān)...
    喵喵_387c閱讀 130評(píng)論 0 0

友情鏈接更多精彩內(nèi)容