前言
單純爲了解決問題,請直接下跳轉【最佳實踐->第三種方案】。
Spring MVC使用時候wield便於數據傳輸,都會使用Model進行接收參數,見實例:
請求:
127.0.0.1:8080/test?myName=zs&myAge=10
接收請求:
@GetMapping("/demo")
public R<String> query(Demo demo) {
// 調用具體服務
service.server(demo);
return R.ok(“業務處理完畢”);
}
接收實體:
class Demo {
private String myName;
private String myAge;
public void setMyName(String myName) {
this.myName = myName;
}
public void setMyAge(String myAge) {
this.myAge = myAge;
}
}
那如果請求的變成下列這樣(帶了字符),在不改變接收參數的開發習慣下(駝峯命名)該怎麼解決?
127.0.0.1:8080/test?my_name=zs&my_age=10
最佳實踐
對於這種的127.0.0.1:8080/test?my_name=zs&my_age=10請求,有多種解決方案:
第一種:使用@RequestParam
見實例,這種的比較笨,需要手動賦值,顯然沒有交給Spring MVC去處理來的輕鬆。
@GetMapping("/demo")
public R<String> query(@RequestParam(name = "my_name", required = false) String myName,
@RequestParam(name = "my_age", required = false) String myAge) {
final Demo demo = new Demo();
demo.setMyName(myName);
demo.setMyAge(myAge);
// 調用具體服務
service.server(demo);
return R.ok("業務處理完畢");
}
第二種:改變setter方法
見下圖,接收仍然是對象,儘管能運行,但強迫症使我接收不了啊,不論是屬性還是Setter方法變得太難受了,怎麼辦,最佳實踐肯定有好的解決辦法,見第三。
@GetMapping("/demo")
public R<String> query(Demo demo) {
// 調用具體服務
service.server(demo);
return R.ok(“業務處理完畢”);
}
class Demo {
private String myName;
// 直接賦值
private String my_age;
// 通過setter賦值
public void setMy_name(String myName) {
this.myName = myName;
}
}
第三種:使用@ConstructorProperties
使用@ConstructorProperties標記構造方法即可完美解決問題,強迫症終於治癒了
public R<String> query(Demo demo) {
// 調用具體服務
service.server(demo);
return R.ok(“業務處理完畢”);
}
class Demo {
private String myName;
private String myAge;
@ConstructorProperties({"my_name", "my_age"})
public Demo(String myName, String myAge) {
this.myName = myName;
this.myAge = myAge;
}
public void setMyName(String myName) {
this.myName = myName;
}
public void setMyAge(String myAge) {
this.myAge = myAge;
}
}
源碼截圖
Spring MVC解析參數並綁定對象的處理類:org.springframework.web.method.annotation.ModelAttributeMethodProcessor
具體方法:
constructAttribute(java.lang.reflect.Constructor<?>, java.lang.String, org.springframework.core.MethodParameter, org.springframework.web.bind.support.WebDataBinderFactory, org.springframework.web.context.request.NativeWebRequest)
// 通過構造方法構建,然而這裏並沒有做任何事,固定返回null
Object constructed = constructAttribute(ctor, attributeName, binderFactory, webRequest);
if (constructed != null) {
return constructed;
}
// 構造方法是個無慘構造方法
if (ctor.getParameterCount() == 0) {
// 那麼只能通過setter 或 直接屬性賦值了,對應改變setter 和屬性值
return BeanUtils.instantiateClass(ctor);
}
// 沒錯,就是這裏了通過有參構造器注入,同時呢,他會去看一個註解,
// 就是我們用的@ConstructorProperties,當然如果請求參數和構造參數完全一致,那就不需要該註解了。
ConstructorProperties cp = ctor.getAnnotation(ConstructorProperties.class);
String[] paramNames = (cp != null ? cp.value() : parameterNameDiscoverer.getParameterNames(ctor));
Assert.state(paramNames != null, () -> "Cannot resolve parameter names for constructor " + ctor);
Class<?>[] paramTypes = ctor.getParameterTypes();
...省略...
return BeanUtils.instantiateClass(ctor, args);