springboot 同一請求入口,根據不同入參用不同實體類接收&調用不同接口實現類(枚舉、泛型、多態綜合運用)


1.情景展示

  請求入參:

  這是一個對外提供的請求總入口,入參interfaceMethod對應不同的接口名稱,具體的接口請求參數封裝到xcParams里面。

  對外只提供這一個接口,而不是不同接口提供不同地址,這樣一來,無論是接口提供方還是接口調用方只要遵循這種規范,就可以完成不同接口的調用,也利於后期接口的啟用、禁用、擴展新接口,提高系統的可維護性。

  像這樣,用實體類接收到請求入參,獲取將要調取的接口方法,根據不同接口名稱匹配調用不同的業務實現類進行業務處理。

2.現狀分析

  在實際開發過程中,公司與公司之間或者公司內部項目與項目之間往往會存在這種需求,公司A提供接口,公司B調用接口,如果是多個接口,那么就可以像上面那樣搞一個總入口:

  使用switch判斷具體需要調用哪一個接口,以及負責處理的業務實現類就可以了。

  以上的代碼是完全沒有問題的,已經滿足了實際業務需要,這種入門級的代碼,基本不要動什么腦子;

  但是,如果我們想要使用高逼格的代碼實現這種功能,能夠讓我們用更多java知識應用到實際開發過程中,學以致用,使自己的能力得到升華,換句話說就是:想裝X,請欣賞下面的高逼格代碼。

  (其實,對於產品來說,不管你是低級代碼還是高級代碼實現,只要能滿足產品需求就是OK的,這一點我們一定要擺好自己位置,不能沾沾自喜)

3.高級代碼實現

  先看效果

  請求入參使用了泛型控制,不同接口自動映射到對應的實體類去接收(上圖)

  調對應接口時,一行代碼搞定,無需手動加判斷該調哪個業務實現類。

  想實現這種效果,就繼續往下看哈。

  用實體類接收請求入參是本文的重中之重,先來看一下

  第一,注解@Getter、@Setter;

  使用的是lombok插件,其作用是生成私有屬性的Get、Set方法,不想用或者沒有的,自己手動生成替代就可以了(類的屬性私有化,再對外提供Get和Set方法,其實就是java三大特性之一:封裝)。

  第二,注解@ApiModelProperty;

  對請求參數進行介紹,使用的是knife4j,為接口提供的API文檔,最終效果如下圖所示:

 

  這樣一來,接口的請求參數、響應參數都有詳細介紹,與別人對接接口的時候再也不用一遍一遍的跟不同的人解釋了,把文檔地址扔給他,清晰明了。

  注意:這不是本文介紹的重點,心有余力的,見文末推薦文章。

  第三,注解@JsonProperty;

  反序列化:將json對象的key與實體類的指定屬性對照起來,並完成賦值操作;

  @JsonProperty,隸屬於Jackson,springboot內部集成,請求入參轉實體類默認使用的就是Jackson;

  由於將請求參數與泛型進行映射,所以,每個屬性都需要加上@JsonProperty注解進行對照。

  第四,注解@NotBlank

  字符串非空校驗:只用在String上,表示傳進來的值不能為null,而且調用trim()后,長度必須大於0,即:必須有實際字符。

  說明:泛型無法使用注解進行非空校驗,所以,上圖中的注解@NotNull無效

  第五,用枚舉類來接收請求入參的其中一個參數值;

  在這里,使用枚舉類CzInterfaceEnum來接收請求參數InterfaceMethod的值,這個說法並不對,因為枚舉類往往不止有一個屬性,而InterfaceMethod卻只有一個值,按正常邏輯來想,json是完全不能實現發序列化的。

  這我們就不得不提及注解@JsonValue啦

  被加上注解@JsonValue的屬性,將會完成值的映射,也就是InterfaceMethod的值最終會被賦值到枚舉類的interfaceName屬性上,這樣就沒有一點問題了。

  第六,往枚舉類注入對象;

  如果枚舉類只有這一點點作用,那我們也不用廢這么大勁用它接收,干脆用String類來接收豈不方便?

  如果這樣想,就大錯特錯了,再來回想一下,如果我們用String類來接收InterfaceMethod的值,那我們豈不是又回到了原點:后續還得用switch來判斷,進而調用不同的業務實現類。

  之所以想用枚舉類來接收InterfaceMethod,是因為,這個時候,我們已經拿到了InterfaceMethod的值,換句話說,我們這個時候就已經知道,它調用的是哪個接口,該用哪個接口實現類來處理!

  難點在於:如何往枚舉類注入對象?

  見文末推薦。

  第七,不同接口的請求參數使用不同實體類接收;

  通過注解@JsonTypeInfo和@JsonSubTypes結合實現

  由最開始的時候,我們得知:不同的接口雖然請求參數不同,但都會塞到CzParams這個參數中,所以,我們就能根據參數InterfaceMethod來判斷當前請求該用哪個請求實體類來接收;

  所以,CzParams的類型只能用泛型來接收(只有在運行調用的時候,才能知道它是誰);

  這樣一來,請求實體類只需繼承抽象類CzRequestParams,並在該父類中設置@JsonSubType.Type注解即可。

  如此,就能實現剛開始提到的效果;

  對於高頻接口,還可以開啟多線程,解決並發問題(本文未做展示)。

4.多態的另一種用法

  2020-12-15

  關於用泛型接收類的方式,其實也是可以去掉的,只是,可能會讓看代碼的人一臉懵逼。

  上面說的很清楚,這個地方用的是泛型,並在class類上用了泛型限定<T extends CzRequestParams>,這樣方便其它人員維護,看到立馬就知道實際接收參數的是CzRequestParams的子類。

  既然是子類,那我們何不用的干脆一點?

  把泛型干掉,直接用父類接收完事(向上泛型),實際負責接收的還是那些子類。

5.嵌套實體類校驗不生效問題

2020-12-16

  使用@Valid或@Validator注解進行相關檢驗時,會造成嵌套類上相關校驗不生效的問題(嵌套驗證),比如:

  CzRequestDto類能夠使用注解進行校驗,這沒有一點問題。

  問題是:CzRequestDto類里又使用其他實體類接收請求參數,例如:上圖中的泛型,它對應的其中一個實體類是下面這個類,

  這就是所謂的嵌套。

  在這個類里面,使用參數校驗注解,不會生效。

  如何解決這個問題?

  這個問題困擾了我很久,其實很簡單,在嵌套實體類的地方,再次聲明校驗注解就完事了。

 

寫在最后

  哪位大佬如若發現文章存在紕漏之處或需要補充更多內容,歡迎留言!!!

 相關推薦:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM