Spring Boot+原生注解@JsonView 輕松過(guò)濾字段,真的優(yōu)雅!
前后端分離的項(xiàng)目中,使用Json字符串來(lái)完成前后端之間的通信。在默認(rèn)情況下,只要前端發(fā)起請(qǐng)求,就會(huì)返回對(duì)象的所有字段。但有的時(shí)候,后端不能把所有字段全部返回。
一方面,是因?yàn)橛行┳侄吻岸瞬恍枰祷剡^(guò)多的數(shù)據(jù)會(huì)占用網(wǎng)絡(luò)帶寬;
另一方面是出于安全性考慮,比如,不可以將密碼返回給前端,否則,網(wǎng)站攻擊者可以用REST工具直接獲取密碼。
而JsonView的作用,就是用來(lái)控制C層返回哪些字段的。
@JsonView 它是 Jackson 庫(kù)中的一個(gè)強(qiáng)大注解。通過(guò)定義不同的視圖類(通常為接口或類),并在實(shí)體類的字段上使用 @JsonView 注解標(biāo)記該字段在哪個(gè)視圖中可見(jiàn),同時(shí)在控制器方法中通過(guò) @JsonView 注解指定返回的視圖,就可以靈活控制返回的字段內(nèi)容,實(shí)現(xiàn)了根據(jù)不同場(chǎng)景動(dòng)態(tài)選擇序列化字段的目的,大大提高了代碼的簡(jiǎn)潔性和可維護(hù)性。
JsonView的效果
通過(guò)以下幾個(gè)實(shí)例的對(duì)比,來(lái)展示JsonView的效果。
以下的代碼,我們通過(guò)一個(gè)小Demo來(lái)演示:
在這個(gè)小小的教務(wù)系統(tǒng)中,有三種實(shí)體——教師、學(xué)生、班級(jí),
為了清晰的展示實(shí)體關(guān)系,提供簡(jiǎn)單的E-R圖:
圖片
由圖可知: 班級(jí)和教師是多對(duì)一的關(guān)系, 學(xué)生和班級(jí)是多對(duì)一的關(guān)系 因此,如果查詢學(xué)生,班級(jí)會(huì)包含在學(xué)生的字段中,教師會(huì)包含在班級(jí)的字段中。 這是典型的“對(duì)象套對(duì)象套對(duì)象”的例子。
下面實(shí)體的代碼供參考(可略過(guò)):
圖片
1、不使用JsonView
在不使用JsonView的情況下,使用REST工具直接取出一個(gè)學(xué)生,返回了學(xué)生的所有字段,也包含關(guān)聯(lián)查詢得到的對(duì)象:
{
"id":1,
"name":"學(xué)生1",
"sno":"123456",
"klass":{
"id":1,
"teacher":{
"id":1,
"name":"張三",
"sex":false,
"username":"zhangsan",
"email":"123@123.com"
},
"name":"班級(jí)1"
}
}
因此,很容易得到結(jié)論一:
不使用JsonView時(shí),返回所有字段,包括外鍵關(guān)聯(lián)對(duì)象的所有字段
2、在實(shí)體的字段上使用JsonView
在原來(lái)的基礎(chǔ)上,對(duì)姓名和學(xué)號(hào)字段分別使用JsonView,同時(shí),定義對(duì)應(yīng)的接口:
圖片
在C層控制器中加入NameJsowView: (注意:此時(shí)只有姓名,并沒(méi)有加入學(xué)號(hào)和班級(jí))
圖片
返回結(jié)果如下:
{
"name":"學(xué)生1"
}
由于我們?cè)贑層只使用了姓名的字段,除了姓名,其他字段均不返回。關(guān)注公眾號(hào):碼猿技術(shù)專欄,回復(fù)關(guān)鍵詞:1111 獲取阿里內(nèi)部Java性能調(diào)優(yōu)手冊(cè)!
因此可以得出結(jié)論二:
對(duì)于已經(jīng)定義JsowView的對(duì)象,C層只返回注解中的JsonView接口里面包含的字段,其他字段一概不返回。
3、在實(shí)體的關(guān)聯(lián)對(duì)象中使用JsonView
前一節(jié)的基礎(chǔ)上,把C層的注解由 **@JsonView(Student.NameJsonView.class)改為@JsonView(Student.KlassJsonView.class)**來(lái)返回學(xué)生對(duì)象關(guān)聯(lián)的班級(jí)。
圖片
這次的返回結(jié)果比較有意思,只返回了空的Klass,里面一個(gè)字段也沒(méi)有:
{
"klass":
{
}
}
所以,結(jié)論三:通過(guò)外鍵關(guān)聯(lián)的對(duì)象,使用JsonView只會(huì)返回關(guān)聯(lián)空對(duì)象本身,而不返回關(guān)聯(lián)對(duì)象的任何字段。
4、在關(guān)聯(lián)對(duì)象的字段中使用JsonView
為了解決結(jié)論三的問(wèn)題,我們需要像上文一樣,在學(xué)生關(guān)聯(lián)的班級(jí)實(shí)體中也啟用JsonView。
然后新建一個(gè)接口,分別繼承班級(jí)字段以及班級(jí)實(shí)體中的姓名、教師等其他字段:
public interface GetByIdJsonView extends Student.KlassJsonView, Klass.NameJsonView, Klass.TeacherJsonView {}
把這個(gè)新接口寫到C層方法的注解上:
@GetMapping("{id}")
@JsonView(GetByIdJsonView.class)
public Student getById(@PathVariable Long id) {
return this.studentService.findById(id);
}
再次運(yùn)行,查看返回結(jié)果:
{
"klass":
{
"teacher":{ },
"name":"班級(jí)1"
}
}
符合預(yù)期,因此,結(jié)論四:
如果想返回關(guān)聯(lián)對(duì)象中的字段,只需要繼承這個(gè)實(shí)體中,相關(guān)字段的JsonView接口即可。
細(xì)心的你可以發(fā)現(xiàn),Teacher中依然沒(méi)有字段,如果也想返回Teacher的字段,只需要在接口中繼續(xù)繼承即可。
JsonView的使用方法
接下來(lái)說(shuō)具體如何在Spring的項(xiàng)目中應(yīng)用JsonView。
1、在實(shí)體類中定義接口
需要記住接口名稱
// 定義了一個(gè)接口,用于JsonView控制返回字段
public interface SnoJsonView {}
2、在實(shí)體中的字段中加入注解
找到一個(gè)字段,加入**@JsonView(XXXJsonView.class)**,名稱與剛才寫的接口名稱相同。
@JsonView(SnoJsonView.class)
private String sno;
3、繼承接口
實(shí)際的項(xiàng)目中,不可能只返回一個(gè)字段,如果返回多個(gè)字段,那就在C層再定一個(gè)接口,繼承所以要返回字段的接口即可。 原則上,每個(gè)控制器方法,都必須有唯一的JsonView接口,接口名與方法名相同,不能混用。
定義一個(gè)與C層方法名相同的接口,繼承業(yè)務(wù)邏輯中需要返回的所有字段:
public interface GetByIdJsonView extends Student.KlassJsonView, Student.NameJsonView, Student.SnoJsonView {}
4、在C層方法上加入注解
最后一步,就是把剛才的接口,加到要控制字段的C層方法上:
@GetMapping("{id}")
@JsonView(GetByIdJsonView.class)
public Student getById(@PathVariable Long id) {
return this.studentService.findById(id);
}
到此,就可以實(shí)現(xiàn)用JsonView控制返回字段了。 這種做法的優(yōu)點(diǎn)在于: 實(shí)體層中,接口名和字段名一致,到C層引用時(shí),就可以根據(jù)名稱知道這個(gè)接口控制哪個(gè)字段; 控制器中,接口名與方法名一致,通過(guò)接口名可以知道是這個(gè)方法返回哪些字段。
總結(jié)
前后端分離的項(xiàng)目中,使用Json字符串來(lái)完成前后端之間的通信,但有的時(shí)候,后端不能把所有字段全部返回,因此可以使用JsonView,來(lái)控制C層返回哪些字段。
如果不使用JsonView,默認(rèn)返回所有字段,包括外鍵關(guān)聯(lián)對(duì)象的所有信息; 如果使用JsonView,只返回接口中聲明的所有字段,如果出現(xiàn)關(guān)聯(lián)對(duì)象,只返回關(guān)聯(lián)對(duì)象本身,而不返回其中的字段。 JsonView接口可以通過(guò)繼承,來(lái)實(shí)現(xiàn)返回不同字段的組合。