11.27 SpringBoot版本竟然引發這種問題,讓我吐血三升

引言

不知道大家是不是也有一種想法,就是喜歡用新的東西。比如:手機系統出現新版本就趕緊升級、軟件出現新版本也會在第一時間進行升級。反正我是有這種想法,比較喜歡新的東西,因為新的東西會有更 cool 的特性,可以給人心理、生理上一種舒適感(生理舒適感???)。

背景介紹

公司不同項目使用的 SpringBoot 版本是不同的,最近在做的項目使用的是比較新的版本,2.x。該項目開發過程中,所有對外提供的服務,都會有一個接口層和接口實現層。為了讓接口的職責更加的清晰明瞭,大部分信息都在接口層進行定義。比如:接口的請求映射路徑,接口方法接收參數的方式@RequestBody 以及參數校驗方式@Valid 等等。後來又去另一個項目組去開發,接到一個需要提供服務接口的任務,當時也沒在意,覺得很簡單,分分鐘就可以完成,悲劇也就在此時此刻發生了。

接下來請大家先看看項目實例代碼,在實例中會為大家重現錯誤,並提供解決方案以及分析出現該問題的原理是為什麼。

項目實例

第一步:我們需要定義服務的接口

<code>@RequestMapping("/persons")
public interface PersonApi {
/**
* add
*
* @param person
* @return
*/

@PostMapping("/")
List<person> add(@Valid @RequestBody Person person);

/**
* update
*
* @param person
* @return
*/
@PutMapping("/")
List<person> update(@Valid @RequestBody Person person);
}
/<person>/<person>/<code>

第二步:有了服務接口,那麼肯定會有服務接口的實現

<code>@RestController
public class PersonController implementsPersonApi{

private static List<person> personList = new ArrayList<>;

static {
personList.add(new Person(10001, "test1"));
personList.add(new Person(10002, "test2"));
personList.add(new Person(10003, "test3"));
personList.add(new Person(10004, "test4"));
personList.add(new Person(10005, "test5"));
}

@Override
public List<person> add(Person person) {
personList.add(person);
return personList;
}

@Override
public List<person> update(Person person) {
personList.removeIf(p -> Objects.equals(p.getId, person.getId));
personList.add(person);
return personList;
}
}

/<person>/<person>/<person>/<code>

第三步:服務接口編寫完成,就需要自己調用接口進行測試,接下來就進行接口測試

原创 | SpringBoot版本竟然引发这种问题,让我吐血三升

可以看到接口可以正常調用,說明接口上使用@RequestBody 起了作用。當然,本文到此還沒有結束,好戲還在後面

第五步:開始展示真正的本領了,修改 SpringBoot 的版本號,將版本號修改為 1.5.8.RELEASE

第六步:再次接口調用

原创 | SpringBoot版本竟然引发这种问题,让我吐血三升

可以很神奇的看到,接口請求的參數並沒有和我們的接口參數對象進行綁定,也就是我們的@RequestBody 註解不生效了。此時的我整個人都不好了,上一個項目寫的好好的接口,到這裡就不行了?難道是水土不服?於是打開了另一個項目,一個字母一個字母進行對照,生怕錯誤了什麼,最終還是以我失敗宣告結束。

嘗試方法

當時是不想看源碼的,因為看源碼好累,於是我就在實現方法上也加上了同樣的註解,準備嘗試一下。

<code>@Override
public List<person> add(@RequestBody Person person) {
personList.add(person);
return personList;
}
/<person>/<code>

不抱希望的嘗試請求接口

當返回結果出現在我眼前的時候,只有“我靠”兩個字能形容我的心情。突然有一種山重水複疑無路,柳暗花明又一村的感覺,整個世界又變的美好了。

雖然問題得到了完美解決,但是內心深處還是想了解下到底是為什麼?只有弄清原理,下次在遇到此問題的時候,我們才可能迎刃而解,不費吹灰之力。接下來就為大家揭開這層神秘的面紗!

疑惑解答

SpringBooot1.5.8.RELEASE 源碼分析

打開 RequestMappingHandlerAdapter#invokeHandlerMethod,找到如下代碼

<code>modelFactory.initModel(webRequest, mavContainer, invocableMethod);
/<code>

一直到 MethodParameter#getParameterAnnotations

原创 | SpringBoot版本竟然引发这种问题,让我吐血三升

如上代碼獲取當前方法參數的註解信息,最終返回,重點:此處獲取的是實現方法上的註解,並不會獲取接口方法上的註解,所以必須在實現方法上

加上註解。

SpringBooot2.2.0.RELEASE 源碼分析

跟著上面的思路找到獲取參數註解的方法

原创 | SpringBoot版本竟然引发这种问题,让我吐血三升

可以很清楚的看到升級後的 SpringBoot 的 HandlerMethodParameter 中重寫了獲取參數註解的方法,重寫方法中調用了父類獲取參數註解的方法,並且在自己的實現中又去獲取了接口上的註解,然後進行組合。因此升級後的項目中可以在接口上定義註解,同樣也可以在實現方法上定義註解。

此次的此已經結束了,後續還會繼續為大家帶來精彩對決的吐血經歷,讓擼友多點發質,多點開心。

參考文獻

  • 搭上 SpringBoot 請求處理源碼分析專車

  • 搭上 SpringBoot 參數解析返回值處理源碼分析專車

———— e n d ————

快年底了,師長為大家準備了三份面試寶典:

  • 《java面試寶典5.0》

  • 《350道Java面試題:整理自100+公司》

  • 《資深java面試寶典-視頻版》

分別適用於初中級,中高級,以及資深級工程師的面試複習。

內容包含java基礎、javaweb、各個性能優化、JVM、鎖、高併發、反射、Spring原理、微服務、Zookeeper、數據庫、數據結構、限流熔斷降級等等。

一、初中級《java面試寶典5.0》,對標8-13K

原創 | SpringBoot版本竟然引發這種問題,讓我吐血三升

二、中高級《350道Java面試題:整理自100+公司》,對標12-20K

原創 | SpringBoot版本竟然引發這種問題,讓我吐血三升

三、資深《java面試突擊-視頻版》,對標20K+

原創 | SpringBoot版本竟然引發這種問題,讓我吐血三升
原創 | SpringBoot版本竟然引發這種問題,讓我吐血三升


分享到:


相關文章: