kotlin1.2升级1.3踩坑
Table of Contents
现象
在对项目的kotlin版本从1.2升级到1.3的时候,有一个很奇怪的现象。我觉得值得一提。当然,也不得不感谢大佬的博客让我受益匪浅。 现象是这样的,项目的kotlin版本从1.2升级到1.3之后,在idea中debug启动是正常启动的。但是当我用jar包启动时,就会报错 Ambiguous mapping. 然后,代码是没有改动的,就仅仅改动到一些pom文件。 tips: . 大概意思就是mapping重复了。
分析
1.2能正常工作,1.3就报错。不会是字节码在搞鬼吧。 看看这个报错的类生成的字节码。 kotlin1.2
kotlin1.3
看起来就只有一个差异了,1.2多了一个bridge。 不会是这个原因吧。 到这里好像也并没有什么思路,我怎么知道这个bridge究竟是怎么影响到mapping处理的呢。所以还是得看看mapping的处理过程。
SpringMVC处理Mapping
接下来应该去看看springmvc是怎么处理Mapping的。这一个咋一看,好像没什么思路,只有google。也没什么答案。但是呢,作为一个程序员,解决问题不能全靠google吧。还是要学会正向解决问题。记得之前梳理过spring的启动过程。从实现思路来看,应该会在初始化类之后,解析对应的mapping放入到一个map中。这个从报错日志也能发现端倪。
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry.assertUniqueMethodMapping(AbstractHandlerMethodMapping.java:576) ~[spring-webmvc-4.3.9.RELEASE.jar!/:4.3.9.RELEASE]
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry.register(AbstractHandlerMethodMapping.java:540) ~[spring-webmvc-4.3.9.RELEASE.jar!/:4.3.9.RELEASE]
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.registerHandlerMethod(AbstractHandlerMethodMapping.java:264) ~[spring-webmvc-4.3.9.RELEASE.jar!/:4.3.9.RELEASE]
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.detectHandlerMethods(AbstractHandlerMethodMapping.java:250) ~[spring-webmvc-4.3.9.RELEASE.jar!/:4.3.9.RELEASE]
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.initHandlerMethods(AbstractHandlerMethodMapping.java:214) ~[spring-webmvc-4.3.9.RELEASE.jar!/:4.3.9.RELEASE]
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.afterPropertiesSet(AbstractHandlerMethodMapping.java:184) ~[spring-webmvc-4.3.9.RELEASE.jar!/:4.3.9.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping.afterPropertiesSet(RequestMappingHandlerMapping.java:127) ~[spring-webmvc-4.3.9.RELEASE.jar!/:4.3.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687) ~[spring-beans-4.3.9.RELEASE.jar!/:4.3.9.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624) ~[spring-beans-4.3.9.RELEASE.jar!/:4.3.9.RELEASE]
... 24 more
从这个流程来看的话,大概率是检测handlerMethods会不一致,导致有bridge标志符的会被过滤掉,我们就来细看这个detectHandlerMethods方法。 果然,detectHandlerMethods内部有个MethodIntrospector.selectMethods调用,这个内部用了ReflectionUtils.USER_DECLARED_METHODS。 里面就是判断哪些方法需要进入mapping map的。到这里,我们就能知道,为啥kotlin1.2版本没有这个问题,而kotlin1.3编译的字节码会存在这个问题了。
那怎么解决呢?
按这个逻辑来讲,kotlin1.3和spring-webmvc.4.3.11不是水火不容吗? 这样的话,应该有两种解决方式,要么spring解决非桥接方法问题,要么kotlin解决mapping问题。总不能我重写spring的类吧
Spring解决方式
让我们来看看spring5.x的ReflectionUtils.USER_DECLARED_METHODS。多了一个isSynthetic,刚好kotlin1.3也有这个AccessFlag.
Kotlin解决方式
kotlin升级到1.4.21也能解决,原因是getQuestionList$default没有mapping了
总结
- Idea可能会骗人,字节码永远不会
- 不熟悉流程时,调用链非常关键。结合处理流程和调用链报错,反猜问题根因是一个不错的方式。