SpringMvc4.1:注解JsonView与泛型返回类

来源:互联网 发布:b站视频mac 编辑:程序博客网 时间:2024/06/03 18:33

前段时间,因工作需要,要做一个WEB层,放在展示层(HTML,JS,移动端)和服务层(DubboX)中间,使用JSON暴露数据给展示层。经过一番调研,决定使用SpringMVC4.1.6+Jackson2.5.1来搭建此项目。

 

常规搭建略去不提,因为用的是SpringMVC4.1以上的版本,因此决定在返回JSON数据的时候,使用新注解@JsonView来支持同一个VO显示不同的JSON视图,其目的主要是隐藏VO中的敏感字段,节约流量,减少不必要字段输出,方便展示层(JS)处理。框架搭建过程很顺利,但在返回封装好的泛型JSON类的时候,出现了问题,即泛型属性对象在指定JsonView的视图之后渲染失败。经过对Jackson源码的初步浏览和跟踪,终于找到问题所在,现将解决过程总结在此。


问题描述:

 

JsonView使用分为三步:
1:定义view相关接口。
2:在VO的属性上使用JsonView注解,此为定义阶段,此步可以和第一步放在一起。
3:在Action的方法上使用JsonView注解,此为使用阶段。

 
首先,

定义VO如下,同时在VO里面定义了两个接口(IOnlyIdView ,IOnlyIdView)用于显示不同JSON视图:

[java] view plaincopy
  1. public final class Depot {  
  2.     public interface IOnlyIdView {}  
  3.     public interface IOnlyNameView{}  
  4.   
  5.     @JsonView(IOnlyIdView.class)  
  6.     private long id;  
  7.   
  8.     @JsonView(IOnlyNameView.class)  
  9.     private String name;  
  10.   
  11.     private long num;  
  12.   
  13.     public long getId() {  
  14.         return id;  
  15.     }  
  16.     public void setId(long id) {  
  17.         this.id = id;  
  18.     }  
  19.     public String getName() {  
  20.         return name;  
  21.     }  
  22.     public void setName(String name) {  
  23.         this.name = name;  
  24.     }  
  25.     public long getNum() {  
  26.         return num;  
  27.     }  
  28.     public void setNum(long num) {  
  29.         this.num = num;  
  30.     }  
  31. }  

然后,定义Action的方法如下:

[java] view plaincopy
  1. @RequestMapping(value = "/{id}", method = RequestMethod.GET, produces = {"application/json;charset=UTF-8"})  
  2. @JsonView(Depot.IOnlyNameView.class)//定义不同的view 隐藏掉一些字段  
  3. public Depot getDepotInfo(@PathVariable long id) {  
  4.     Depot depot = new Depot();  
  5.     depot.setId(100);  
  6.     depot.setName("北京001仓");  
  7.     depot.setNum(888);  
  8.     return depot;  
  9. }<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>  

 

则将项目运行之后结果打印(说明Depot的JsonView视图生效):

{"name":"北京001仓"}
 
将该方法getDepotInfo返回值改为List<Depot> ,方法注解不变,方法体改为

[java] view plaincopy
  1. Depot depot = new Depot();  
  2. depot.setId(100);  
  3. depot.setName("北京001仓");  
  4. depot.setNum(888);  
  5. List<Depot> ret = new ArrayList<>();  
  6. ret.add(depot);  
  7. return ret;  
 

则将项目运行之后结果打印(说明放在集合容器中,JsonView视图生效):

[{"name":"北京001仓"}]
 
 
   将该方法返回值改为JsonResult<Depot>,方法注解不变,方法体改为:
[java] view plaincopy
  1. depot.setId(100);  
  2. depot.setName("北京001仓");  
  3. depot.setNum(888);  
  4. List<Depot> ret = new ArrayList<>();  
  5. ret.add(depot);  
  6. return new JsonResult(ret,true,"ok");  


则将项目运行之后结果打印:
{}
 
JsonResult是我们架构设计中统一返回的类。所有VO都要放到该对象中返回。
 

JsonResult的定义如下:

[java] view plaincopy
  1. public class JsonResult<T> implements Serializable {  
  2.     private static final long serialVersionUID = 3863559687276427577L;  
  3.       
  4.     private boolean success = true;  
  5.   
  6.     public String message;  
  7.   
  8.     private String secure;//安全凭据  
  9.   
  10.     private T data;//数据  
  11.   
  12.     public JsonResult() {  
  13.     }  
  14.     public JsonResult(T data, Boolean success, String message) {  
  15.         // TODO Auto-generated constructor stub  
  16.         this.data = data;  
  17.         this.success = success;  
  18.         this.message = message;  
  19.     }  
  20.     public boolean isSuccess() {  
  21.         return success;  
  22.     }  
  23.     public void setSuccess(boolean success) {  
  24.         this.success = success;  
  25.     }  
  26.     public T getData() {  
  27.         return data;  
  28.     }  
  29.     public void setData(T data) {  
  30.         this.data = data;  
  31.     }  
  32.     public String getMessage() {  
  33.         return message;  
  34.     }  
  35.     public void setMessage(String message) {  
  36.         this.message = message;  
  37.     }  
  38.     public static <T> JsonResult<T> newResult() {  
  39.         return new JsonResult<T>();  
  40.     }  
  41.     public String getSecure() {  
  42.         return secure;  
  43.     }  
  44.     public void setSecure(String secure) {  
  45.         this.secure = secure;  
  46.     }  
  47. }  


 
问题解决:
 

SpringMVC 将VO本身以及和Action上的JsonView 注解内容传递给Jackson,由Jackson在渲染视图时进行处理。经过跟踪,Jackson在解析不同的java对象的时候,使用不同的Serializer来把对象转换为json串。比如当Action返回值为Depot对象的时候,Jackson使用基础的BeanSerializer来处理,而返回值为List<Depot>的时候,则使用IndexedListSerializer来解析。

我们也可以通过在类上加注解 @JsonSerialize(using=CustomSerializer.class)来自定义某个类的Serializer。本例中,经过初步的跟踪,发现BeanSerializer在处理JsonResult的时候,如果JsonResult的属性不含JsonView视图接口,则不去过滤该属性所包含的所有视图。所以,要为JsonResult属性也定义一个视图注解,并且,视图注解接口要被泛型vo里面的属性视图接口所继承,才能实现整体视图的正常渲染和过滤。有点拗口,看解决范例吧。呵呵


为JsonResult定义一个视图接口,如下:

[java] view plaincopy
  1. public class GeneralViews {  
  2.     /** 
  3.      * use in error views 
  4.      */  
  5.     public interface IErrorView{};  
  6.   
  7.     /** 
  8.      * use in success views 
  9.      */  
  10.     public interface INormalView extends IErrorView{} ;  
  11. }  


则JsonResult类改造为:
[java] view plaincopy
  1. public class JsonResult<T> implements Serializable {  
  2.     private static final long serialVersionUID = 3863559687276427577L;  
  3.   
  4.     @JsonView(GeneralViews.IErrorView.class)  
  5.     private boolean success = true;  
  6.   
  7.     @JsonView(GeneralViews.IErrorView.class)  
  8.     public String message;  
  9.   
  10.     @JsonView(GeneralViews.INormalView.class)  
  11.     private String secure;//安全凭据  
  12.   
  13.     @JsonView(GeneralViews.INormalView.class)  
  14.     private T data;//数据  
  15.   
  16.     public JsonResult() {  
  17.     }  
  18.   
  19.     public JsonResult(T data, Boolean success, String message) {  
  20.     // TODO Auto-generated constructor stub  
  21.     this.data = data;  
  22.     this.success = success;  
  23.     this.message = message;  
  24.     }  
  25.   
  26.     public boolean isSuccess() {  
  27.     return success;  
  28.     }  
  29.   
  30.     public void setSuccess(boolean success) {  
  31.     this.success = success;  
  32.     }  
  33.   
  34.     public T getData() {  
  35.     return data;  
  36.     }  
  37.   
  38.     public void setData(T data) {  
  39.     this.data = data;  
  40.     }  
  41.   
  42.     public String getMessage() {  
  43.     return message;  
  44.     }  
  45.   
  46.     public void setMessage(String message) {  
  47.     this.message = message;  
  48.     }  
  49.   
  50.     public static <T> JsonResult<T> newResult() {  
  51.     return new JsonResult<T>();  
  52.     }  
  53.   
  54.     public String getSecure() {  
  55.     return secure;  
  56.     }  
  57.   
  58.     public void setSecure(String secure) {  
  59.     this.secure = secure;  
  60.     }  
  61. }  

最后,所用的业务VO改为:
[java] view plaincopy
  1.        public final class Depot {  
  2.     public interface IOnlyIdView extends GeneralViews.INormalView{}  
  3.     public interface IOnlyNameView extends GeneralViews.INormalView{}  
  4.   
  5.   
  6.     @JsonView(IOnlyIdView.class)  
  7.     private long id;  
  8.   
  9.   
  10.     @JsonView(IOnlyNameView.class)  
  11.     private String name;  
  12.   
  13.   
  14.     private long num;  
  15.   
  16.   
  17.     public long getId() {  
  18.     return id;  
  19.     }  
  20.   
  21.   
  22.     public void setId(long id) {  
  23.     this.id = id;  
  24.     }  
  25.   
  26.   
  27.     public String getName() {  
  28.     return name;  
  29.     }  
  30.   
  31.   
  32.     public void setName(String name) {  
  33.     this.name = name;  
  34.     }  
  35.   
  36.   
  37.     public long getNum() {  
  38.     return num;  
  39.     }  
  40.   
  41.   
  42.     public void setNum(long num) {  
  43.     this.num = num;  
  44.     }  
  45. }  


再次运行Action,则结果为:
{"success":true,"message":"ok","secure":null,"data":[{"name":"北京001仓"}]} 
 
最终,通过这样的方式,成功实现了JsonView视图在泛型属性对象上的传递。
0 0
原创粉丝点击