Skip to content

Ambiguous mapping error when using generic interface [SPR-16288] #20835

Closed
@spring-projects-issues

Description

@spring-projects-issues

Robert Thornton opened SPR-16288 and commented

I've upgraded my project from Spring Boot 1.5.8.RELEASE (using Spring Framework 4.3.12) to 1.5.9.RELEASE (using Spring Framework 4.3.13), and I am now getting an ambiguous mapping error on startup.

Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'articleController' method 
public org.springframework.data.domain.Page demo.ArticleController.find(org.springframework.data.domain.Pageable,demo.EntityPredicate) throws java.io.IOException
to {[/v1/articles],methods=[GET],params=[page]}: There is already 'articleController' bean method
public org.springframework.data.domain.Page<demo.Article> demo.ArticleController.find(org.springframework.data.domain.Pageable,demo.ArticlePredicate) mapped.
	at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry.assertUniqueMethodMapping(AbstractHandlerMethodMapping.java:576) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry.register(AbstractHandlerMethodMapping.java:540) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.registerHandlerMethod(AbstractHandlerMethodMapping.java:264) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.detectHandlerMethods(AbstractHandlerMethodMapping.java:250) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.initHandlerMethods(AbstractHandlerMethodMapping.java:214) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.afterPropertiesSet(AbstractHandlerMethodMapping.java:184) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.afterPropertiesSet(RequestMappingHandlerMapping.java:127) ~[spring-webmvc-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624) ~[spring-beans-4.3.13.RELEASE.jar:4.3.13.RELEASE]

I've isolated the cause and simplified it to the following sample code that reproduces the issue:

package demo;

import java.io.IOException;
import java.time.Instant;
import java.util.List;
import java.util.UUID;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import static demo.ApiConstants.ARTICLES_PATH;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

@RestController
@RequestMapping(path = ARTICLES_PATH)
class ArticleController implements ApiConstants, ResourceEndpoint<Article, ArticlePredicate> {

    @GetMapping(params = "page")
    public Page<Article> find(Pageable pageable, ArticlePredicate predicate) {
        throw new UnsupportedOperationException("not implemented");
    }

    @GetMapping
    public List<Article> find(Sort sort, ArticlePredicate predicate) {
        throw new UnsupportedOperationException("not implemented");
    }
}

interface ApiConstants {

    String API_V1 = "/v1";
    String ARTICLES_PATH = API_V1 + "/articles";
}

interface ResourceEndpoint<E extends Entity, P extends EntityPredicate> {

    Page<E> find(Pageable pageable, P predicate) throws IOException;

    List<E> find(Sort sort, P predicate) throws IOException;
}

abstract class Entity {

    public UUID id;
    public String createdBy;
    public Instant createdDate;
}

class Article extends Entity {

    public String slug;
    public String title;
    public String content;
}

abstract class EntityPredicate<E extends Entity> {

    public String createdBy;
    public Instant createdBefore;
    public Instant createdAfter;

    public boolean accept(E entity) {
        return (createdBy == null || createdBy.equals(entity.createdBy)) &&
                (createdBefore == null || createdBefore.compareTo(entity.createdDate) >= 0) &&
                (createdAfter == null || createdAfter.compareTo(entity.createdDate) >= 0);
    }
}

class ArticlePredicate extends EntityPredicate<Article> {

    public String query;

    @Override
    public boolean accept(Article entity) {
        return super.accept(entity) && (query == null || (entity.title.contains(query) || entity.content.contains(query)));
    }
}

The ArticleController class implements two interfaces: ApiConstants, and ResourceEndpoint. The ResourceEndpoint interface is a generic interface that accepts type parameters for the resource entity and a predicate. If I remove the ApiConstants interface, the error goes away, suggesting that Spring MVC may be confused when the controller implements more than one interface. I've verified that the above code still works on Spring Boot 1.5.8 (using Spring Framework 4.3.12)

The above sample code doesn't demonstrate why I'm using the two interfaces. Its intent is simply to demonstrate the regression in functionality.


Affects: 4.3.13

Issue Links:

Referenced from: pull request #1632, and commits 347c2da, 69c882c, 121f9e3

Backported to: 4.3.14

Metadata

Metadata

Assignees

Labels

in: webIssues in web modules (web, webmvc, webflux, websocket)status: backportedAn issue that has been backported to maintenance branchestype: regressionA bug that is also a regression

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions