2018년 11월 12일 월요일

Kotlin Spring Ambiguous mapping. Cannot map * method 문제

혹시나 kotlin + spring1.5.x 버전으로 개발을 하다가 intellij의 kotlin plugin을 update 하고 나서
Ambiguous mapping. Cannot map '*' method 에러가 난다면 이 이슈도 확인해보자.
(특히 console build는 되는데 intellij에서 build가 안된다면 이 문제일 가능성이 높다.)

증상
intellij에 있는 kotlin plugin을 "1.3.0-release-IJ2018.2-1"으로 업데이트 했더니 잘 뜨던 spring에 문제가 생겨서 확인해봤다.
문제의 위치는 refundController의 expire 메소드였는데 이상하니 httpMethod가 EXPIRE인 function만 문제가 되었다.


Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'refundController' method 
public static org.springframework.hateoas.Resource com.titicacacorp.billing.domain.management.refund.controller.RefundController.expire$default(com.titicacacorp.billing.domain.management.refund.controller.RefundController,long,java.lang.String,int,java.lang.Object)
to {[/management/refunds/{refundId}],methods=[DELETE]}: There is already 'refundController' bean method
public org.springframework.hateoas.Resource com.titicacacorp.billing.domain.management.refund.controller.RefundController.expire(long,java.lang.String) mapped.
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1631)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
 at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:481)
 at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312)
 at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
 at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308)
 at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
 at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:208)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.addCandidateEntry(DefaultListableBeanFactory.java:1314)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1280)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1178)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1094)
 at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1064)
 at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:835)
 at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741)
 ... 36 common frames omitted
Caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'refundController' method 
 

에러를 보면 Ambiguous mapping으로 이미 어딘가에서 refundController의 expire 메소드를 이미 mapping 했다고 에러가 난 것이다.

처음에는 이것 때문에 매핑룰이 문제인가 해서 룰을 고쳐보았으나 해결되진 않았고 원인은 뜻하지 않는 곳에 있었다.


    @DeleteMapping("/refunds/{refundId}")
    fun expire(
        @PathVariable refundId: Long,
        @RequestParam(required = false) operation: String? = null
    ): Resource {
        refundService.expire(refundId, operation)

        return Resource(refundId)
    }
 
expire 메소드에 대한 코드는 위처럼 생겼는데 문제가 생긴 부분은 뜬금없이 RequestParam이었다.
http delete method의 경우 requestbody에 데이터를 보낼 수 없기 때문에 queryString으로 간단한 데이터를 전송하려고 만들었는데.. kotlin plugin 버전이 올라가면서 여기부분에 처리 문제가 생긴 듯 하다.

        @RequestParam(required = false) operation: String? = null)

위 코드에서 null일 때 default 변수를 세팅하는 로직을 지우면 해결된다.


    fun expire(
        @PathVariable refundId: Long,
        @RequestParam(required = false) operation: String?)


이렇게 말이다.

아마 plugin의 path가 진행되면 해당 문제도 사라질 것이지만 혹시나해서 글로 남긴다.

댓글 없음:

댓글 쓰기