【问题标题】:SwaggerSpringMvcPlugin not working on Spring MVCSwaggerSpringMvcPlugin 不适用于 Spring MVC
【发布时间】:2015-01-11 03:27:36
【问题描述】:

我在我的 REST 后端项目中使用以下组件:

  • Spring MVC:4.1.2.RELEASE(最新)
  • Hibernate4 对象映射器:2.4.3(最新)
  • 招摇:0.9.1(最新)
  • Javaee-web-api: 7.0 (servlet 3.1)

我正在使用基于 XML 的 bean 配置,我能够使用 Spring XML 配置文件中的以下内容启动并运行 Swagger 的基本设置:

 <mvc:annotation-driven/> <!-- Required so swagger-springmvc can access spring's RequestMappingHandlerMapping  -->

 <bean class="com.mangofactory.swagger.configuration.SpringSwaggerConfig" />

然后,我从 https://github.com/wordnik/swagger-ui 克隆了 Swagger-UI GIT 存储库,并将 dist 文件夹复制到我的 /webapp/docs 文件夹中。这样做之后,我可以在以下 URL 上使用基于 JS 的 UI:

http://localhost:9090/docs/index.html (works so far).

问题在于,try it out 按钮都不起作用,这些按钮可用于直接使用 JSON 与您的 REST API 进行交互。它不起作用的原因是它没有采用正确的 base url path 与我的控制器对话:

http://localhost:9090/rest/addresses (actual controller location)
http://localhost:9090/addresses (swagger's attempt to talk to the API)

在网上进行一些研究后,我发现我可能最好采用更灵活的方法,使用 https://github.com/martypitt/swagger-springmvc 上记录的 SwaggerSpringMvcPlugin。

这是我的 swagger 配置类文件:

@Configuration
@EnableSwagger
public class MySwaggerConfig {

    private SpringSwaggerConfig springSwaggerConfig;

    @Autowired
    public void setSpringSwaggerConfig(SpringSwaggerConfig springSwaggerConfig) {
        this.springSwaggerConfig = springSwaggerConfig;
    }

    @Bean
    public SwaggerSpringMvcPlugin customImplementation(){
        return new SwaggerSpringMvcPlugin(this.springSwaggerConfig)
                .includePatterns(".*");
    }
}

这是我的完整 spring mvc xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/util
                           http://www.springframework.org/schema/util/spring-util.xsd
                           http://www.springframework.org/schema/mvc
                           http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <context:component-scan base-package="be.exampley.backend.web.rest"/>

    <bean id="hibernateAwareObjectMapper" class="be.example.backend.util.HibernateAwareObjectMapper"/>

    <mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"
                  p:prettyPrint="true"
                  p:supportedMediaTypes-ref="supportedMediaTypes"
                  p:objectMapper-ref="hibernateAwareObjectMapper"/>
        </mvc:message-converters>
    </mvc:annotation-driven>

    <util:list id="supportedMediaTypes">
        <value>application/json</value>
        <value>text/plain</value>
    </util:list>

    <context:annotation-config/>

    <mvc:default-servlet-handler/>

    <context:property-placeholder location="classpath:swagger.properties"/>

    <bean name="swaggerConfig" class="be.example.backend.configuration.MySwaggerConfig"/>

</beans>

我的 web.xml 文件中相关部分的摘录:

<servlet>
    <servlet-name>restServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>WEB-INF/spring/mvc-rest-config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>restServlet</servlet-name>
    <url-pattern>/rest/*</url-pattern>
</servlet-mapping>

这是我的 swagger.properties 文件,由于某种原因根本没有考虑到它。

documentation.services.basePath=/rest/
documentation.services.version=2.0

我在应用程序启动期间得到的堆栈跟踪:

    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'mySwaggerConfig': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void be.example.backend.configuration.MySwaggerConfig.setSpringSwaggerConfig(com.mangofactory.swagger.configuration.SpringSwaggerConfig); nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.mangofactory.swagger.configuration.SpringSwaggerConfig': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private java.util.List com.mangofactory.swagger.configuration.SpringSwaggerConfig.handlerMappings; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping] found for dependency [collection of org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1204)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:538)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:229)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:725)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
    at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106)
    at org.eclipse.jetty.server.handler.ContextHandler.callContextInitialized(ContextHandler.java:798)
    at org.eclipse.jetty.servlet.ServletContextHandler.callContextInitialized(ServletContextHandler.java:444)
    at org.eclipse.jetty.server.handler.ContextHandler.startContext(ContextHandler.java:789)
    at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:294)
    at org.eclipse.jetty.webapp.WebAppContext.startWebapp(WebAppContext.java:1341)
    at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1334)
    at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:741)
    at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:497)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
    at org.eclipse.jetty.server.handler.ContextHandlerCollection.doStart(ContextHandlerCollection.java:163)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
    at org.eclipse.jetty.server.Server.start(Server.java:387)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
    at org.eclipse.jetty.server.Server.doStart(Server.java:354)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.runner.Runner.run(Runner.java:509)
    at org.eclipse.jetty.runner.Runner.main(Runner.java:557)
...
Caused by: 
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping] found for dependency [collection of org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1261)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:961)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:904)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:527)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1204)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:538)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:229)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1081)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1006)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:904)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:600)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1204)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:538)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:229)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:298)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:725)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:757)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:480)
    at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:403)
    at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:306)
    at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:106)
    at org.eclipse.jetty.server.handler.ContextHandler.callContextInitialized(ContextHandler.java:798)
    at org.eclipse.jetty.servlet.ServletContextHandler.callContextInitialized(ServletContextHandler.java:444)
    at org.eclipse.jetty.server.handler.ContextHandler.startContext(ContextHandler.java:789)
    at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:294)
    at org.eclipse.jetty.webapp.WebAppContext.startWebapp(WebAppContext.java:1341)
    at org.eclipse.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1334)
    at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:741)
    at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:497)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
    at org.eclipse.jetty.server.handler.ContextHandlerCollection.doStart(ContextHandlerCollection.java:163)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:132)
    at org.eclipse.jetty.server.Server.start(Server.java:387)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:61)
    at org.eclipse.jetty.server.Server.doStart(Server.java:354)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
    at org.eclipse.jetty.runner.Runner.run(Runner.java:509)
    at org.eclipse.jetty.runner.Runner.main(Runner.java:557)

我在某处读到 No qualifying bean of type org...RequestMappingHandlerMapping 跟踪表明我可能会在我的 XML 中错过 mvc:annotation-driven,但这不是情况。

我一直在寻找几个小时来寻找解决方案,但到目前为止都失败了。事实上,在它的基本配置中(使用 SpringSwaggerConfig)它的工作率为 90%(除了 try it out 按钮...),这让我不想放弃大摇大摆。它看起来真的很酷,是记录 API 的好方法。

因此,如果有人可以帮助我解决这个问题,我将非常感激。大摇大摆的摇滚!

亲切的问候, 巴特

【问题讨论】:

    标签: rest spring-mvc swagger swagger-ui


    【解决方案1】:

    我可能偶然发现了以下问题,“没有为 RequestMappingHandlerMapping 找到匹配的 bean 进行自动装配”:http://forum.spring.io/forum/spring-projects/web/112154-unable-to-autowire-requestmappinghandlermapping-in-controller

    与此同时,我已切换到 基于 Java 的配置 来替换我的 XML。这也解决了自动连线问题。特此我的解决方案供其他人参考,其中包括更改 apiResourcePrefix 并使用 swagger.properties 文件进行外部配置。

    我的 web.xml 文件:

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring/business.xml</param-value>
    </context-param>
    
    <servlet>
        <servlet-name>restServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextClass</param-name>
            <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
        </init-param>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>be.example.backend.configuration.MvcConfig</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>restServlet</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>
    

    我的 bussiness.xml 作为根应用程序上下文:

    <context:annotation-config/>
    <context:component-scan base-package="be.example.backend"/>
    ...
    

    我的 MvcConfig.java 作为 MVC 配置文件:

    @EnableWebMvc
    @ComponentScan(basePackages = {"be.example.backend"})
    @Configuration
    public class MvcConfig extends WebMvcConfigurerAdapter {
    
        @Override
        public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
            List<MediaType> supportedMediaTypes = new ArrayList<>();
            supportedMediaTypes.add(MediaType.APPLICATION_JSON);
            supportedMediaTypes.add(MediaType.TEXT_PLAIN);
    
            MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
            converter.setObjectMapper(new HibernateAwareObjectMapper());
            converter.setPrettyPrint(true);
            converter.setSupportedMediaTypes(supportedMediaTypes);
            converters.add(converter);
    
            super.configureMessageConverters(converters);
        }
    }
    

    我的 SwaggerConfig.java 作为 swagger 配置文件:

    @EnableWebMvc
    @EnableSwagger
    @PropertySource("classpath:swagger.properties")
    @ComponentScan(basePackages = {"be.example.backend"})
    @Configuration
    public class SwaggerConfig implements ServletContextAware {
    
        @Autowired
        private Environment environment;
    
        private SpringSwaggerConfig springSwaggerConfig;
    
        private ServletContext servletContext;
    
        @Autowired
        public void setSpringSwaggerConfig(SpringSwaggerConfig springSwaggerConfig) {
            this.springSwaggerConfig = springSwaggerConfig;
        }
    
        @Bean
        public SwaggerSpringMvcPlugin customImplementation() {
            RelativeSwaggerPathProvider relativeSwaggerPathProvider = new RelativeSwaggerPathProvider(servletContext);
            relativeSwaggerPathProvider.setApiResourcePrefix(environment.getProperty("swagger.resource_prefix"));
            return new SwaggerSpringMvcPlugin(this.springSwaggerConfig)
                    .pathProvider(relativeSwaggerPathProvider);
        }
    
    
        @Override
        public void setServletContext(ServletContext servletContext) {
            this.servletContext = servletContext;
        }
    }
    

    我的 swagger.properties 外部属性文件:

    swagger.resource_prefix=rest
    

    这个解决方案对我有用,我现在可以通过 swagger-ui 享受 swagger 功能。

    希望其他人也能喜欢!

    【讨论】:

      【解决方案2】:

      我有完全相同的异常

          org.springframework.beans.factory.BeanCreationException: 
          Error creating bean with name 'mySwaggerConfig': Injection of autowired dependencies failed; 
      nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: 
      public void be.example.backend.configuration.MySwaggerConfig.setSpringSwaggerConfig
      (com.mangofactory.swagger.configuration.SpringSwaggerConfig)
      

      在我的测试中。根本原因是注释不正确。我的测试使用 @Integration 进行注释,而不是使用 @WebIntegrationTest

      对于生产代码,请确保您的 @Configuration 使用 @EnableWebMvc

      进行注释

      【讨论】:

        猜你喜欢
        • 2018-04-19
        • 1970-01-01
        • 2018-05-30
        • 2017-07-15
        • 2018-05-22
        • 1970-01-01
        • 2019-06-29
        • 2015-02-23
        • 1970-01-01
        相关资源
        最近更新 更多