自定义 RequestMappingHandlerMapping 导致 Swagger 出错

Custom RequestMappingHandlerMapping causes an error with Swagger

我已经使用最新版本的 Spring 创建了自定义 RequestMappingHandlerMapping,如下所示,因此我可以定义自定义版本注释。

网络配置,

@Configuration
@ConditionalOnClass({ ApiVersionedResource.class })
public class WebConfiguration  {

    @Bean
    @Primary
    public ContentNegotiationManager contentNegotiationManager() {
        ContentNegotiationManagerFactoryBean contentNegotiationManagerFactoryBean = new ContentNegotiationManagerFactoryBean();
        Map<String, MediaType> mediaTypes = new HashMap<>();
        mediaTypes.put("json", MediaType.APPLICATION_JSON);
        contentNegotiationManagerFactoryBean.addMediaTypes(mediaTypes);
        contentNegotiationManagerFactoryBean.afterPropertiesSet();
        return contentNegotiationManagerFactoryBean.getObject();
    }

    @Bean
    public WebMvcRegistrationsAdapter webMvcRegistrationsHandlerMapping() {
        return new WebMvcRegistrationsAdapter() {
            @Override
            public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
                CustomRequestMappingHandlerMapping handlerMapping = new CustomRequestMappingHandlerMapping();
                handlerMapping.setOrder(0);
                handlerMapping.setRemoveSemicolonContent(false);
                handlerMapping.setContentNegotiationManager(contentNegotiationManager());
                return handlerMapping;
            }
        };
    }
}

CustomRequestMappingHandlerMapping,

public class CustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
    @Override
    protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
        ApiVersionedResource typeAnnotation = AnnotationUtils.findAnnotation(handlerType, ApiVersionedResource.class);
        return createCondition(typeAnnotation);
    }

    @Override
    protected RequestCondition<?> getCustomMethodCondition(Method method) {
        ApiVersionedResource methodAnnotation = AnnotationUtils.findAnnotation(method, ApiVersionedResource.class);
        return createCondition(methodAnnotation);
    }

    @Override
    protected boolean isHandler(Class<?> beanType) {
        return super.isHandler(beanType) && (AnnotationUtils.findAnnotation(beanType, ApiVersionedResource.class) != null);
    }

    private RequestCondition<?> createCondition(ApiVersionedResource versionMapping) {
        if (versionMapping != null) {
            return new ApiVersionedResourceRequestCondition(versionMapping.media(), versionMapping.version());
        }

        return null;
    }
}

然而,这在编译过程中导致了以下错误,我不知道是什么原因造成的。

2017-03-15 16:44:58.215 DEBUG 63518 --- [  restartedMain] r.h.v.CustomRequestMappingHandlerMapping : 6 request handler methods found on class com.test.web.rest.OrganizationResource: {public org.springframework.http.ResponseEntity com.test.web.rest.OrganizationResource.createOrganization(com.test.service.dto.OrgDetailsDTO) throws java.net.URISyntaxException={[/organizations],methods=[POST],custom=[API Versions = {media=application/vnd.test.onboarding, versions=[]}]}, public org.springframework.http.ResponseEntity com.test.web.rest.OrganizationResource.updateOrganization(com.test.service.dto.OrgDetailsDTO) throws java.net.URISyntaxException={[/organizations],methods=[PUT],custom=[API Versions = {media=application/vnd.test.onboarding, versions=[]}]}, public org.springframework.http.ResponseEntity com.test.web.rest.OrganizationResource.getAllOrganizations(org.springframework.data.domain.Pageable) throws java.net.URISyntaxException={[/organizations],methods=[GET],custom=[API Versions = {media=application/vnd.test.onboarding, versions=[]}]}, public org.springframework.http.ResponseEntity com.test.web.rest.OrganizationResource.deleteOrganization(java.lang.Long)={[/organizations/{id}],methods=[DELETE],custom=[API Versions = {media=application/vnd.test.onboarding, versions=[]}]}, public org.springframework.http.ResponseEntity com.test.web.rest.OrganizationResource.searchOrganizations(java.lang.String,org.springframework.data.domain.Pageable) throws java.net.URISyntaxException={[/search/organizations],methods=[GET],custom=[API Versions = {media=application/vnd.test.onboarding, versions=[]}]}, public org.springframework.http.ResponseEntity com.test.web.rest.OrganizationResource.getOrganization(java.lang.Long)={[/organizations/{id}],methods=[GET],custom=[API Versions = {media=application/vnd.test.onboarding, versions=[]}]}}
2017-03-15 16:44:58.238  INFO 63518 --- [  restartedMain] r.h.v.CustomRequestMappingHandlerMapping : Mapped "{[/organizations],methods=[POST],custom=[API Versions = {media=application/vnd.test.onboarding, versions=[]}]}" onto public org.springframework.http.ResponseEntity<com.test.service.dto.OrgDetailsDTO> com.test.web.rest.OrganizationResource.createOrganization(com.test.service.dto.OrgDetailsDTO) throws java.net.URISyntaxException
2017-03-15 16:44:58.239  INFO 63518 --- [  restartedMain] r.h.v.CustomRequestMappingHandlerMapping : Mapped "{[/organizations],methods=[PUT],custom=[API Versions = {media=application/vnd.test.onboarding, versions=[]}]}" onto public org.springframework.http.ResponseEntity<com.test.service.dto.OrgDetailsDTO> com.test.web.rest.OrganizationResource.updateOrganization(com.test.service.dto.OrgDetailsDTO) throws java.net.URISyntaxException
2017-03-15 16:44:58.239  INFO 63518 --- [  restartedMain] r.h.v.CustomRequestMappingHandlerMapping : Mapped "{[/organizations],methods=[GET],custom=[API Versions = {media=application/vnd.test.onboarding, versions=[]}]}" onto public org.springframework.http.ResponseEntity<java.util.List<com.test.service.dto.OrgDetailsDTO>> com.test.web.rest.OrganizationResource.getAllOrganizations(org.springframework.data.domain.Pageable) throws java.net.URISyntaxException
2017-03-15 16:44:58.239  INFO 63518 --- [  restartedMain] r.h.v.CustomRequestMappingHandlerMapping : Mapped "{[/organizations/{id}],methods=[DELETE],custom=[API Versions = {media=application/vnd.test.onboarding, versions=[]}]}" onto public org.springframework.http.ResponseEntity<java.lang.Void> com.test.web.rest.OrganizationResource.deleteOrganization(java.lang.Long)
2017-03-15 16:44:58.240  INFO 63518 --- [  restartedMain] r.h.v.CustomRequestMappingHandlerMapping : Mapped "{[/search/organizations],methods=[GET],custom=[API Versions = {media=application/vnd.test.onboarding, versions=[]}]}" onto public org.springframework.http.ResponseEntity<java.util.List<com.test.service.dto.OrgDetailsDTO>> com.test.web.rest.OrganizationResource.searchOrganizations(java.lang.String,org.springframework.data.domain.Pageable) throws java.net.URISyntaxException
2017-03-15 16:44:58.240  INFO 63518 --- [  restartedMain] r.h.v.CustomRequestMappingHandlerMapping : Mapped "{[/organizations/{id}],methods=[GET],custom=[API Versions = {media=application/vnd.test.onboarding, versions=[]}]}" onto public org.springframework.http.ResponseEntity<com.test.service.dto.OrgDetailsDTO> com.test.web.rest.OrganizationResource.getOrganization(java.lang.Long)
2017-03-15 16:44:58.314  WARN 63518 --- [  restartedMain] ationConfigEmbeddedWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'documentationPluginsBootstrapper' defined in URL [jar:file:/Users/kasunt/.m2/repository/io/springfox/springfox-spring-web/2.6.1/springfox-spring-web-2.6.1.jar!/springfox/documentation/spring/web/plugins/DocumentationPluginsBootstrapper.class]: Unsatisfied dependency expressed through constructor parameter 1; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'webMvcRequestHandlerProvider' defined in URL [jar:file:/Users/kasunt/.m2/repository/io/springfox/springfox-spring-web/2.6.1/springfox-spring-web-2.6.1.jar!/springfox/documentation/spring/web/plugins/WebMvcRequestHandlerProvider.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'requestMappingHandlerMapping' is expected to be of type 'org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping' but was actually of type 'com.sun.proxy.$Proxy232'

特别是在最后,

***************************
APPLICATION FAILED TO START
***************************

Description:

The bean 'requestMappingHandlerMapping' could not be injected as a 'org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping' because it is a JDK dynamic proxy that implements:


Action:

Consider injecting the bean as one of its interfaces or forcing the use of CGLib-based proxies by setting proxyTargetClass=true on @EnableAsync and/or @EnableCaching.

我似乎已经通过重构 WebConfiguration 解决了这个问题,如下所示。我不完全确定为什么将 CustomRequestMappingHandlerMapping 实例化为 public class 不起作用。我很乐意听取任何能想到原因的人的意见。

@Configuration
@ConditionalOnClass({ ApiVersionedResource.class })
public class WebConfiguration {

    @Bean
    public WebMvcRegistrationsAdapter webMvcRegistrationsHandlerMapping() {
        return new WebMvcRegistrationsAdapter() {
            @Override
            public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
                return new CustomRequestMappingHandlerMapping();
            }
        };
    }

    private final class CustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {

        @Override
        protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
            ApiVersionedResource typeAnnotation = AnnotationUtils.findAnnotation(handlerType, ApiVersionedResource.class);
            return createCondition(typeAnnotation);
        }

        @Override
        protected RequestCondition<?> getCustomMethodCondition(Method method) {
            ApiVersionedResource methodAnnotation = AnnotationUtils.findAnnotation(method, ApiVersionedResource.class);
            return createCondition(methodAnnotation);
        }

        @Override
        protected boolean isHandler(Class<?> beanType) {
            return super.isHandler(beanType) && (AnnotationUtils.findAnnotation(beanType, ApiVersionedResource.class) != null);
        }

        private RequestCondition<?> createCondition(ApiVersionedResource versionMapping) {
            if (versionMapping != null) {
                return new ApiVersionedResourceRequestCondition(versionMapping.media(), versionMapping.version());
            }

            return null;
        }
    }
}

在Spring Boot 2.0.0 中,有一种更简单的方法可以实现这一点。

创建一个 WebMvcRegistrations 接口的实例作为 bean 并覆盖适当的方法以 return 该对象的自定义版本。 Spring 启动将读取并使用该实例。

在这种情况下,只需要覆盖 getRequestMappingHandlerMapping() 和自定义实现 returned