SwaggerSpringMvcPlugin不适用于Spring MVC

时间:2014-11-13 20:49:02

标签: rest spring-mvc swagger swagger-ui

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

  • Spring MVC:4.1.2.RELEASE(最新)
  • Hibernate4 Object Mapper:2.4.3(最新版)
  • Swagger:0.9.1(最新)
  • Javaee-web-api:7.0(servlet 3.1)

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

 <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).

问题是试用按钮都没有工作,可以直接使用JSON与REST API进行交互。它不起作用的原因是它没有采用正确的基本URL路径来与我的控制器通信:

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

经过一些网上研究后我发现,我最好采用更加灵活的方法,使用SwaggerSpringMvcPlugin,如https://github.com/martypitt/swagger-springmvc所述。

这是我的 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)

我在某处读到没有类型组织的合格bean ... RequestMappingHandlerMapping 跟踪表明我可能会错过我的XML中的 mvc:annotation-driven ,但这不是案件。

我现在一直在寻找解决方案,但到目前为止失败了。事实上,在它的基本配置中(使用 SpringSwaggerConfig ),它的工作率为90%(除了试用按钮...),让我不想放弃招摇。它看起来非常酷,是记录API的好方法。

所以,如果有人能帮助我解决这个问题,我将非常感激。摇摇晃晃的岩石!

亲切的问候, 巴特

2 个答案:

答案 0 :(得分:6)

我可能会遇到以下问题,&#34;没有找到匹配的自动接线豆#34;对于RequestMappingHandlerMapping:http://forum.spring.io/forum/spring-projects/web/112154-unable-to-autowire-requestmappinghandlermapping-in-controller

同时我已切换到基于Java的配置来替换我的XML。这也解决了autowire问题。因此我的解决方案是其他参考,包括更改 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享受这种骄傲的功能。

希望其他人也能享受它!

答案 1 :(得分: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)

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

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

进行注释