6

前言

在从学习转换到实际生产项目中,学到了教程中没有的知识,实际生产项目更加贴近于实际,用到了更多的方法。就比如Java的一个注解@JsonView。

应用场景

在实际生产项目中,我们在后台返回实体对象转化的json对象时,不希望后台返回实体的一些数据给前台,比如说我们不希望每次返回用户时不想把用户的密码返回到前台。这时候我们就可以用到@JsonView字段。这个注解的作用就是控制输入输出后的json。

定义实体类

在网上看了很多例子,大多数都是拿密码举例,获取用户列表时不需要密码,获取具体某个用户时获取密码,但是我感觉例子不切合实际,理论上我们任何时候都不应该返回密码给前台(有点较真了,人家只是举例而已)。所以我们在此换一个例子。就拿我们现在写的系统来说吧:
1.教师端获取学生作业列表时,获取作业基本信息即可,
2.获取具体某个作业时,获取作业内容。
定义一个作业类

@Entity
public class Work extends AbstractEntity { 

    private Integer score = 0;
    
    @ManyToMany
    @JsonView(AttachmentsJsonView.class)
    private List<Attachment> attachments = new ArrayList<>();

    @ManyToOne
    @JsonView(ItemJsonView.class)
    @JoinColumn(nullable = false)
    private Item item;

    @ManyToOne
    @JsonView(StudentJsonView.class)
    @JoinColumn(nullable = false)
    private Student student;
    
    public interface AttachmentsJsonView {}
    public interface ItemJsonView {}
    public interface StudentJsonView {}
    
    ...
}

我们在此截取
四个字段,成绩,作业内容,作业所在实验,作业所关联学生。我们希望在在获取作业列表时获取作业所在实验信息和作业所关联学生信息从而获取作业基本信息,在查看某一个作业时获取具体作业内容,而我们的作业成绩是我们两个方法都希望返回的。
在作业内容,作业所在实验,作业所关联学生分别加上@JsonView,分别定义接口。 而我们的成绩不需要定义,就会默认所有方法都返回。

定义方法

现在我们来到c层,定义方法我们先来定义接口继承我们在实体类规定的接口,想获取哪些字段就继承对应接口

private interface getAllJsonView extends Work.StudentJsonView, Work.ItemJsonView  {

}

private interface getByIdJsonView extends Work.AttachmentsJsonView {

}

然后方法定义


    /**
     * 获取所有作业
     * @param pageable 分页信息
     * @return 所有作业
     */
    @GetMapping("getAll")
    @JsonView(getAllJsonView.class)
    public Page<Work> getAll(Pageable pageable) {
        return workService.getAll(pageable);
    }
    
    /**
     * 根据id获取作业
     * @param id 作业id
     * @return 作业
     */
    @GetMapping("{id}")
    @JsonView(getByIdJsonView.class)
    public Work getById(@PathVariable Long id) {
        return this.workService.findById(id);
    }

不同方法分别用JsonView不同接口注解,这样就能返回我们想要的啦。

补充

不同方法对应不同接口

此时对于获取具体某个作业再写一个方法

    ...
    
    /**
     * 根据id获取作业
     * @param id 作业id
     * @return 作业
     */
    @GetMapping("{id}")
    @JsonView(getByIdJsonView.class)
    public Work getById(@PathVariable Long id) {
        return this.workService.findById(id);
    }
    
    /**
     * 查看某一学生某一实验作业
     * @param itemId 实验id
     * @param studentId 学生id
     * @return 作业
     */
    @GetMapping("getByItemIdAndStudentId")
    @JsonView(getByItemIdAndStudentIdJsonView.class)
    public Work getByItemIdAndStudentId(@RequestParam Long itemId, @RequestParam Long studentId) {
        Optional<Work> workOptional = workService.getByItemIdAndStudentId(itemId, studentId);
        if (workOptional.isPresent()) {
            throw new ObjectNotFoundException("未找到相关作业");
        }
        return workOptional.get();
    }

我们应该用不同接口去定义,即使我们想要获取的内容是一样的

private interface getByIdJsonView extends Work.AttachmentsJsonView {
    }

private interface getByItemIdAndStudentIdJsonView extends getByIdJsonView {
    }

但是我们可以继承原来的接口,两个内容是一样的。这样写的好处是当我们改变我们的接口时易于维护,假设很多方法都用一个JsonView,当JsonView改变时很多方法都变了,难以维护。

解决栈溢出问题

@JsonView注解还可以解决死循环导致的栈溢出问题,笔者还没遇到过,在此就不细述了。
具体看以下博客

@jsonView过滤属性

总结

在实际生产项目中我们应该考虑更多,也需要用到更多知识。
在此感谢解决我疑惑的学长和老师。
参考博客
@JsonView的使用


小强Zzz
1.2k 声望32 粉丝