SpringMVC过滤,或序列化时指定返回对象的某些属性,以及Jackson的相关注解使用


SpringMVC过滤(指定返回)对象的某些属性

想要了解Jackson相关注解可以直接到第二部分

为什么要过滤对象的某些属性

  1. 场景:在平时写代码的时候会遇到这样的情况,有实体类,User,这里面定义了User相关的所有信息,同时需要对外开放接口查询获得用户的信息,但是要根据信息的不同,写查询不同的信息的接口,分情况返回数据
  2. 我需要用户的基本信息,用户名,等级,头像,个人介绍之类的数据,那么不能将密码,余额之类的返回给前端,那么就需要在返回数据的时候过滤掉不需要返回的属性
  3. 使用工具:SpringMVC以及SpringMVC默认的Json解析,Jackson即可

方法以及代码如下

实体类代码

@Data
public class User {


    private String username;

    private String password;

    private Date brithday;

    private String headImage;

    private Integer account;
    
    private String address;
    
    private String mobile;
}

Controller代码

@RestController
@RequestMapping("/user/")
public class UserController {



    /**
     * 获取用户基础信息
     * @param username
     * @return
     */
    @GetMapping("base")
    public User getUserBaseInfo(String username){
        return User.builder().username("sa").account(213).headImage("http://sss.ss.com").
                address("石家庄").password("123456").mobile("13776684587").build();
    }

    /**
     * 获取用户收货地址信息
     * @param username
     * @return
     */
    @GetMapping("address")
    public User getUserAddressInfo(String username){
        return User.builder().username("sa").account(213).headImage("http://sss.ss.com").
                address("石家庄").password("123456").mobile("13776684587").build();
    }

    /**
     * 获取用户密码信息
     * @param username
     * @return
     */
    @GetMapping("pass")
    public User getUserLoginInfo(String username){
        return User.builder().username("sa").account(213).headImage("http://sss.ss.com").
                address("石家庄").password("123456").mobile("13776684587").build();
    }
}

相关方法以及注解,以及优劣

使用Jackson注解:@JosnIgnore

  1. 该注解一般使用在成员变量上,表示,在Jackson序列化的时候,忽略该属性。
  2. 使用情况:一些不需要返回给前端的属性可以使用该注解忽略掉
@Data
@Builder
public class User {
    private String username;
    @JsonIgnore
    private String password;
    private Date brithday;
    private String headImage;
    @JsonIgnore
    private Integer account;
    @JsonIgnore
    private String address;
    @JsonIgnore
    private String mobile;

}
  1. 将不需要返回的属性在实体类标注出来,即可,弊端:忽略效果,全局起效,每个接口都会忽略掉该属性

使用注解@JsonView

  1. 该注解用于指定,序列化的视图规则,用于两个地方,一个是实体类的属性上,另外一个是controller的接口方法上
  2. 使用:
    2.1 首先需要先建立视图(可以在实体类内部创立,也可以单独创建视图类)
private interface BaseView{}

    @JsonView(User.BaseView.class)
    public String username;
    
    private String password;
    
    @JsonView(User.BaseView.class)
    private Date brithday;
    @JsonView(User.BaseView.class)
    private String headImage;
    
    private Integer account;
    private String address;
    private String mobile;

2.2 在需要返回的属性上面加上JsonView注解并表明,属于那个视图
2.3 在controller接口中指定视图名称即可

 /**
     * 获取用户基础信息
     * @param username
     * @return
     */
    @GetMapping("base")
    @JsonView(User.BaseView.class)
    public User getUserBaseInfo(String username){
        return User.builder().username("sa").account(213).headImage("http://sss.ss.com").
                address("石家庄").password("123456").mobile("13776684587").build();
    }

2.3 这样在访问用户基础信息接口的时候就只会把有@JsonView(User.BaseView.class)注解的属性序列化返回给前端,在访问没有加@JsonView注解的controller时依然还是返回user的全部属性。
3. 这样使用的话,对于一个接口没问题,那其他接口,也想有这个效果呢,那我怎么整?一样的,可以针对另外一个接口也创建一个视图,然后将需要返回的属性上面加上注解,然后对应视图名称,controller上指定对应的视图即可

@Data
@Builder
public class User {
    public interface BaseView{}

    public interface AddressView{}

 @JsonView({User.BaseView.class,User.AddressView.class})
  private String username;
    private String password;
    @JsonView(User.BaseView.class)
    private Date brithday;
    @JsonView(User.BaseView.class)
    private String headImage;
    private Integer account;
    @JsonView(User.AddressView.class)
    private String address;
    @JsonView(User.AddressView.class)
    private String mobile;

}


/**
     * 获取用户基础信息
     * @param username
     * @return
     */
    @GetMapping("base")
    @JsonView(User.BaseView.class)
    public User getUserBaseInfo(String username){
        return User.builder().username("sa").account(213).headImage("http://sss.ss.com").
                address("石家庄").password("123456").mobile("13776684587").build();
    }

    /**
     * 获取用户收货地址信息
     * @param username
     * @return
     */
    @GetMapping("address")
    @JsonView(User.AddressView.class)
    public User getUserAddressInfo(String username){
        return User.builder().username("sa").account(213).headImage("http://sss.ss.com").
                address("石家庄").password("123456").mobile("13776684587").build();
    }
  1. 这样其实很好理解,就相当于我要把实体类中的属性都管理起来,想返回什么,就返回什么,不能多也不能少,这个统一管理的就是JsonView注解,在实体类上加上这个注解,并表明注解对应视图名称,那么就将这个属性纳管到该注解的指定视图中了,在controller时就可以规定返回某个视图纳管下的所有属性,不是这个视图管辖范围内的,不序列化,不返回。

进阶使用(一)

  1. 如果我定义的视图太多了,有些属性是无论那个接口都要返回的,例如username,那么这个属性上面岂不是要加所有的视图名称?
  2. 可以在属性上面加上若干个视图的名称,但如果是公用的,所有接口都返回的,那么就可以先定义一个基础的视图,让其他视图继承这个视图,在公用的属性上面加上基础视图名称即可,因为JsonView(XXX.class)在序列化的时候,不仅能纳管到XXX.class的属性,也可以纳管到XXX父类的属性,代码如下:
@Data
@Builder
public class User {
    public interface  View{}
    public interface BaseView extends View{}
    public interface AddressView extends View{}
    public interface PassView extends View{}
    @JsonView({User.View.class})
    private String username;
    @JsonView(User.PassView.class)
    private String password;
    @JsonView(User.BaseView.class)
    private Date brithday;
    @JsonView(User.BaseView.class)
    private String headImage;
    private Integer account;
    @JsonView(User.AddressView.class)
    private String address;
    @JsonView(User.AddressView.class)
    private String mobile;
}
 //Controller 代码不变

进阶使用(二)

  1. 一般在生产环境中,我们不可能直接在接口中返回一个User对象给前端,我们都会包装一层然后返回。
@Data
@Builder
public  class  ResponseEntity<T> {

    private String message;
    private int status;

    private T t;

    public static <T> ResponseEntity<T> success(T t){
        return (ResponseEntity<T>) ResponseEntity.builder().message("成功!").status(200).t(t).build();
    }
}

Controller代码如下

/**
     * 获取用户密码信息
     * @param username
     * @return
     */
    @GetMapping("pass")
    public ResponseEntity getUserLoginInfo(String username){
        User build = User.builder().username("sa").account(213).headImage("http://sss.ss.com").
                address("石家庄").password("123456").mobile("13776684587").build();
        return ResponseEntity.success(build);
    }
  1. 此时如果直接在controller接口上加上@JsonView(User.PassView.class),你会发现啥也没返回,为什么呢,因为SpringMVC序列化的时候,如果你指定了视图,那么他会以你指定的视图为优先级,去ResponseEntity中 去查看,是否有加了这个视图的属性,发现没有,那么什么都不返回。
  2. 解决方法,就是要在ResponseEntity中加上@JsonView(User.PassView.class)


	@JsonView(User.PassView.class)
    private String message;
    @JsonView(User.PassView.class)
    private int status;
//只在此加的话,只会返回t的属性值,message,以及status依然不会返回
    @JsonView(User.PassView.class)
    private T t;
  1. 新问题,我其他的接口也需要返回ResponseEntity怎么办,那就是继续加,并且把最基础的加上最基础的视图名称,所以最终的代码为:
    User:
@Data
@Builder
public class User {

    public interface  View{}
    public interface BaseView extends View{}
    public interface AddressView extends View{}
    public interface PassView extends View{}
    @JsonView({User.View.class})
    private String username;
    @JsonView(User.PassView.class)
    private String password;
    @JsonView(User.BaseView.class)
    private Date brithday;

    @JsonView(User.BaseView.class)
    private String headImage;

    private Integer account;
    @JsonView(User.AddressView.class)
    private String address;
    @JsonView(User.AddressView.class)
    private String mobile;
}

ResponseEntity:

@Data
@Builder
public  class  ResponseEntity<T> {

    @JsonView(User.View.class)
    private String message;
    @JsonView(User.View.class)
    private int status;

    @JsonView({User.PassView.class,User.AddressView.class,User.BaseView.class})
    private T t;

    public static <T> ResponseEntity<T> success(T t){
        return (ResponseEntity<T>) ResponseEntity.builder().message("成功!").status(200).t(t).build();
    }
}

Controller代码:


    /**
     * 获取用户基础信息
     * @param username
     * @return
     */
    @GetMapping("base")
    @JsonView(User.BaseView.class)
    public ResponseEntity getUserBaseInfo(String username){
        User build = User.builder().username("sa").account(213).headImage("http://sss.ss.com").
                address("石家庄").password("123456").mobile("13776684587").build();
        return ResponseEntity.success(build);
    }

    /**
     * 获取用户收货地址信息
     * @param username
     * @return
     */
    @GetMapping("address")
    @JsonView(User.AddressView.class)
    public ResponseEntity getUserAddressInfo(String username){
        User build = User.builder().username("sa").account(213).headImage("http://sss.ss.com").
                address("石家庄").password("123456").mobile("13776684587").build();
        return ResponseEntity.success(build);
    }

    /**
     * 获取用户密码信息
     * @param username
     * @return
     */
    @GetMapping("pass")
    @JsonView(User.PassView.class)
    public ResponseEntity getUserLoginInfo(String username){
        User build = User.builder().username("sa").account(213).headImage("http://sss.ss.com").
                address("石家庄").password("123456").mobile("13776684587").build();
        return ResponseEntity.success(build);
    }

写到最后,还有其他的用法,读者可以自己下去深入研究,我分享的生产环境使用足够,除非需求比较变态。

Jackson相关注解使用

使用Jackson注解:@JosnIgnore

	1. 该注解一般使用在成员变量上,表示,在Jackson序列化的时候,忽略该属性
	2. 使用情况:一些不需要返回给前端的属性可以使用该注解忽略掉
	3. 弊端:忽略效果,全局起效,每个接口都会忽略掉该属性,但是有时候我们有很多接口,一些属性A接口不需要返回,但是B接口要将该属性返回,此时全部忽略就不适用了

@JsonIgnoreProperties:@JsonIgnore的进阶版

	1. 作用在类上,指定要忽略的属性
	2. @JsonIgnoreProperties(value = {"name","password"})

@JsonProperty

	1. 该注解用在属性上,作用:在jackson序列化的时候,将属性名称改成注解内的属性
	2. 使用情况:返回前端的数据,与实体类的属性名不一样,@JsonProperty("name")
	@JsonProperty("name")
    private String username;
	3. 弊端:全局效果,并且该注解在反序列化的时候也起效,springmvc接收数据也起效

@JsonInclude

	1. 该注解作用在类或者属性上,控制什么情况下属性才能(或不能)被序列化 
	2. 使用情况: @JsonInclude(JsonInclude.Include.NON_NULL)  值不为空的属性,空的属性不序列化
	3. 弊端:只生效于序列化

@JsonPropertyOrder

	1. 该注解作用在类,或者属性上,标识属性序列化的顺序
	2. 使用:一般作用在类上, @JsonPropertyOrder({"name","password","account"}),指定属性序列化顺序
	3. 弊端:只生效于序列化

@JsonSetter

	1. 该注解作用在属性,和setter方法上,在反序列化时,按照注解的配置注入到加注解的属性
	2. 使用:@JsonSetter("name"),类似于JsonProperty
	@JsonSetter("name")
    private String username;
	3. 弊端:仅适用于反序列化的情况

@JsonFormat

	1. 该注解一般用于,服务端将Date类型的属性返回给前端的时候,格式化Date的形式
	2. @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
	3. 仅仅用于序列化的时候,需要反序列化时间格式需要使用 @DateTimeFormat(Spring的注解,用于反序列化时间格式, @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss"))

@JsonView

	1. 该注解用于指定,序列化的视图规则,用于两个地方,一个是实体类的属性上,另外一个是controller的接口方法上
	2. 实体类上:指定这个属性只存在于注解标识的视图类中,controller方法上:指定返回该对象时使用指定的视图类

有问题可以私信我,看到会回答

猜你喜欢

转载自blog.csdn.net/qq_38371367/article/details/102743521