為了減少類文件數量,嘗試將請求參數類定義為 Controller 的非靜態內部類:
@Controller
public class MyController {
// 非靜態內部類
public class QueryParameter {
private String keyword;
// getter/setter...
}
@RequestMapping("/search")
@ResponseBody
public Result search(QueryParameter param) { ... } // 報錯!
}
啟動報錯:
org.springframework.beans.BeanInstantiationException:
Failed to instantiate [com.xxx.MyController$QueryParameter]:
No default constructor found; nested exception is java.lang.NoSuchMethodException:
com.xxx.MyController$QueryParameter.<init>()
Java 編譯器會為非靜態內部類自動生成一個帶有外部類引用參數的構造器:
// 反編譯後的內部類構造器特徵
public MyController$QueryParameter(MyController outer) {
this.outer = outer;
}
Spring MVC 綁定請求參數時,底層調用 BeanUtils.instantiateClass(clazz.getDeclaredConstructor()),默認尋找無參構造器。由於非靜態內部類不存在無參構造器,導致實例化失敗。
public class InnerClassConstructorDemo {
public class Inner { public Inner() {} }
public static void main(String[] args) {
Constructor>[] ctors = Inner.class.getDeclaredConstructors();
for (Constructor> c : ctors) {
System.out.println(c); // 輸出: public InnerClassConstructorDemo$Inner(InnerClassConstructorDemo)
for (Parameter p : c.getParameters()) {
System.out.println(p.getName() + ":" + p.getType()); // outer:class InnerClassConstructorDemo
}
}
}
}
public static class QueryParameter { ... },編譯器不會生成外部類引用參數,保留無參構造器。dto 或 vo 包下,結構清晰,復用性高。Spring MVC 參數綁定依賴無參構造器。非靜態內部類因隱式持有外部類引用,無無參構造器,導致綁定失敗。理解 Java 內部類編譯機制,避開此坑,規範代碼結構。