零、问题的产生
本周须要实现一项工作:在单元测试中实现后端返回字段的断言。
换句话说,须要断言后端向前端返回了哪些字段。
因为对JsonView的理解有余,在找字段的时候破费了较多工夫。
因而本文将从实际的角度,论述JsonView的作用和用法。
一、JsonView的作用
在说作用之前,咱们曾经晓得:前后端拆散的我的项目中,应用Json字符串来实现前后端之间的通信。
在默认状况下,只有前端发动申请,就会返回对象的所有字段。但有的时候,后端不能把所有字段全副返回。
一方面,是因为有些字段前端不须要,返回过多的数据会占用网络带宽;另一方面是出于安全性思考,比方,不能够将明码返回给前端,否则,网站攻击者能够用REST工具间接获取明码。
而JsonView的作用,就是用来管制C层返回哪些字段的。
二、JsonView的成果
通过以下几个实例的比照,来展现JsonView的成果。
以下的代码,咱们通过一个小Demo来演示:
在这个小小的教务零碎中,有三种实体——老师、学生、班级,
为了清晰的展现实体关系,提供简略的E-R图:
由图可知:
班级和老师是多对一的关系,
学生和班级是多对一的关系
因而,如果查问学生,班级会蕴含在学生的字段中,老师会蕴含在班级的字段中。
这是典型的“对象套对象套对象”的例子。
上面实体的代码供参考(可略过):
/** * 班级实体 * 蕴含字段 id、 name、 * 外键 teacher */@Entitypublic class Klass { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne private Teacher teacher; private String name;}
/** * 学生实体 * 蕴含字段 id、 name、 sno * 外键 klass */@Entitypublic class Student { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String name; @Column(nullable = false, unique = true) private String sno; @ManyToOne @JoinColumn(nullable = false) private Klass klass;
/** * 老师实体 * 蕴含字段 id、 name、 sex、 username、 email */@Entitypublic class Teacher { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private Boolean sex; private String username; private String email;
1、不应用JsonView
在不应用JsonView的状况下,应用REST工具间接取出一个学生,返回了学生的所有字段,也蕴含关联查问失去的对象:
{ "id":1, "name":"学生1", "sno":"123456", "klass":{ "id":1, "teacher":{ "id":1, "name":"张三", "sex":false, "username":"zhangsan", "email":"123@123.com" }, "name":"班级1" }}
因而,很容易失去论断一:
不应用JsonView时,返回所有字段,包含外键关联对象的所有字段
2、在实体的字段上应用JsonView
在原来的根底上,对姓名和学号字段别离应用JsonView,同时,定义对应的接口:
public class Student { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // 姓名字段应用JsonView @Column(nullable = false) @JsonView(NameJsonView.class) private String name; // 学号字段应用JsonView @Column(nullable = false, unique = true) @JsonView(SnoJsonView.class) private String sno; // 班级外键应用JsonView @ManyToOne @JsonView(KlassJsonView.class) @JoinColumn(nullable = false) private Klass klass; ... // 姓名字段的接口 public interface NameJsonView {} // 学号字段的接口 public interface SnoJsonView {} // 班级外键对应的实体的接口 public interface KlassJsonView {}}
在C层控制器中退出NameJsowView:
(留神:此时只有姓名,并没有退出学号和班级)
/** * 通过ID查问学生 * @param id 学生ID * @return 学生 */ @GetMapping("{id}") @JsonView(Student.NameJsonView.class) public Student getById(@PathVariable Long id) { return this.studentService.findById(id); }
返回后果如下:
{ "name":"学生1"}
因为咱们在C层只应用了姓名的字段,除了姓名,其余字段均不返回。
因而能够得出结论二:
对于曾经定义JsowView的对象,C层只返回注解中的JsonView接口外面蕴含的字段,其余字段一律不返回。
3、在实体的关联对象中应用JsonView
前一节的根底上,把C层的注解由 @JsonView(Student.NameJsonView.class)
改为
@JsonView(Student.KlassJsonView.class)
来返回学生对象关联的班级。
/** * 通过ID查问学生 * @param id 学生ID * @return 学生 */ @GetMapping("{id}") @JsonView(Student.KlassJsonView.class) public Student getById(@PathVariable Long id) { return this.studentService.findById(id); }
这次的返回后果比拟有意思,只返回了空的Klass,外面一个字段也没有:
{ "klass": { }}
所以,论断三:
通过外键关联的对象,应用JsonView只会返回关联空对象自身,而不返回关联对象的任何字段。
4、在关联对象的字段中应用JsonView
为了解决论断三的问题,咱们须要像上文一样,在学生关联的班级实体中也启用JsonView。
而后新建一个接口,别离继承班级字段以及班级实体中的姓名、老师等其余字段:
public interface GetByIdJsonView extends Student.KlassJsonView, Klass.NameJsonView, Klass.TeacherJsonView {}
把这个新接口写到C层办法的注解上:
@GetMapping("{id}") @JsonView(GetByIdJsonView.class) public Student getById(@PathVariable Long id) { return this.studentService.findById(id); }
再次运行,查看返回后果:
{ "klass": { "teacher":{ }, "name":"班级1" }}
合乎预期,因而,论断四:
如果想返回关联对象中的字段,只须要继承这个实体中,相干字段的JsonView接口即可。
仔细的你能够发现,Teacher中仍然没有字段,如果也想返回Teacher的字段,只须要在接口中持续继承即可。
三、JsonView的应用办法
接下来说具体如何在Spring的我的项目中利用JsonView。
1、在实体类中定义接口
须要记住接口名称
// 定义了一个接口,用于JsonView管制返回字段 public interface SnoJsonView {}
2、在实体中的字段中退出注解
找到一个字段,退出@JsonView(XXXJsonView.class),名称与方才写的接口名称雷同。
@JsonView(SnoJsonView.class) private String sno;
3、继承接口
理论的我的项目中,不可能只返回一个字段,如果返回多个字段,那就在C层再定一个接口,继承所以要返回字段的接口即可。
原则上,每个控制器办法,都必须有惟一的JsonView接口,接口名与办法名雷同,不能混用。
定义一个与C层办法名雷同的接口,继承业务逻辑中须要返回的所有字段:
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); }
到此,就能够实现用JsonView管制返回字段了。
这种做法的长处在于:
实体层中,接口名和字段名统一,到C层援用时,就能够依据名称晓得这个接口管制哪个字段;
控制器中,接口名与办法名统一,通过接口名能够晓得是这个办法返回哪些字段。
四、总结
前后端拆散的我的项目中,应用Json字符串来实现前后端之间的通信,但有的时候,后端不能把所有字段全副返回,因而能够应用JsonView,来管制C层返回哪些字段。
如果不应用JsonView,默认返回所有字段,包含外键关联对象的所有信息;
如果应用JsonView,只返回接口中申明的所有字段,如果呈现关联对象,只返回关联对象自身,而不返回其中的字段。
JsonView接口能够通过继承,来实现返回不同字段的组合。
版权申明
本文作者:河北工业大学梦云智开发团队 - 刘宇轩