Koltin + Spring MVC接口映射重複異

問題分析

在將 SpringBoot 項目的 Kotlin 版本更新至 1.3+ 以後,項目啓動時拋出了以下異常:java

java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'xxxx' methodbash

拋出異常方法是一個有默認參數的方法,形如:app

fun func(str: String = ""){
    // do somethings
}
複製代碼

從 Java 的視覺看,在字節碼反映出的是會生成對應的重載方法。但在 Kotlin1.3 編譯出的字節碼中,這個生成的重載方法相對 Kotlin1.3 相較以前少了一個方法標識 ACC_BRIDGE,即生成的方法再也不是橋接方法。ui

// 使用javap -verbose 查看class文件
// Kotlin1.3 
flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNTHETIC

// Kotlin1.2
flags: ACC_PUBLIC, ACC_STATIC, ACC_BRIDGE, ACC_SYNTHETIC
複製代碼

在 Spring MVC 啓動前,進行 MVC 接口方法和 RequestMapping 的映射註冊,對 @Controller 類下的 Mapping 方法進行掃描並找出真實的 Method 對象,其核心方法以下:spa

// BridgeMethodResolver類方法
public static Method findBridgedMethod(Method bridgeMethod) {
        if (bridgeMethod != null && bridgeMethod.isBridge()) {
            List<Method> candidateMethods = new ArrayList();
            Method[] methods = ReflectionUtils.getAllDeclaredMethods(bridgeMethod.getDeclaringClass());
            Method[] var3 = methods;
            int var4 = methods.length;

            for(int var5 = 0; var5 < var4; ++var5) {
                Method candidateMethod = var3[var5];
                if (isBridgedCandidateFor(candidateMethod, bridgeMethod)) {
                    candidateMethods.add(candidateMethod);
                }
            }

            if (candidateMethods.size() == 1) {
                return (Method)candidateMethods.get(0);
            } else {
                Method bridgedMethod = searchCandidates(candidateMethods, bridgeMethod);
                if (bridgedMethod != null) {
                    return bridgedMethod;
                } else {
                    return bridgeMethod;
                }
            }
        } else {
            return bridgeMethod;
        }
}
複製代碼

由於沒有了 ACC_BRIDGE 標誌,即 isBridge() 會返回 false ,因此默認參數對應的 Method 會進入到 if 分支,對於進入該分支的 Method 對象,通過檢查後會加入備選列表 candidateMethods 中並等待返回。檢查方法邏輯以下:code

private static boolean isBridgedCandidateFor(Method candidateMethod, Method bridgeMethod) {
        return !candidateMethod.isBridge() && !candidateMethod.equals(bridgeMethod) && candidateMethod.getName().equals(bridgeMethod.getName()) && candidateMethod.getParameterTypes().length == bridgeMethod.getParameterTypes().length;
}
複製代碼

顯然,方法會經過檢查並最終完成註冊,這就致使同一個 RequestMapping 下注冊了兩個方法,致使應用的啓動異常。對象

解決方案

  • Kotlin 編譯等級降到 1.2
  • SpringBoot 版本升級到 2.x.x 以上
相關文章
相關標籤/搜索