Search code examples
springspring-bootannotationsspringfox

Custom RequestMappingHandlerMapping causes an error with Swagger


I have created a custom RequestMappingHandlerMapping with the latest version of Spring as follows so I can define a custom version annotation.

WebConfiguration,

@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;
    }
}

However, this is causing the following error during compilation and I have no idea what is causing it.

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'

Specially this right at the end,

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

Solution

  • I seem to have fixed the problem by refactoring WebConfiguration as follows. Im not entirely sure why instantiating CustomRequestMappingHandlerMapping as a public class didn't work. Id be happy to listen to anyone who can think of a reason why.

    @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;
            }
        }
    }