本文共 6011 字,大约阅读时间需要 20 分钟。
前段时间,因工作需要,要做一个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视图:
public final class Depot { public interface IOnlyIdView {} public interface IOnlyNameView{} @JsonView(IOnlyIdView.class) private long id; @JsonView(IOnlyNameView.class) private String name; private long num; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public long getNum() { return num; } public void setNum(long num) { this.num = num; }}
然后,定义Action的方法如下:
@RequestMapping(value = "/{id}", method = RequestMethod.GET, produces = {"application/json;charset=UTF-8"}) @JsonView(Depot.IOnlyNameView.class)//定义不同的view 隐藏掉一些字段 public Depot getDepotInfo(@PathVariable long id) { Depot depot = new Depot(); depot.setId(100); depot.setName("北京001仓"); depot.setNum(888); return depot; }
则将项目运行之后结果打印(说明Depot的JsonView视图生效):
{"name":"北京001仓"} 将该方法getDepotInfo返回值改为List<Depot> ,方法注解不变,方法体改为Depot depot = new Depot(); depot.setId(100); depot.setName("北京001仓"); depot.setNum(888); Listret = new ArrayList<>(); ret.add(depot); return ret;
则将项目运行之后结果打印(说明放在集合容器中,JsonView视图生效):
[{"name":"北京001仓"}] 将该方法返回值改为JsonResult<Depot>,方法注解不变,方法体改为:depot.setId(100); depot.setName("北京001仓"); depot.setNum(888); List则将项目运行之后结果打印: {} JsonResult是我们架构设计中统一返回的类。所有VO都要放到该对象中返回。ret = new ArrayList<>(); ret.add(depot); return new JsonResult(ret,true,"ok");
JsonResult的定义如下:
public class JsonResult问题解决:implements Serializable { private static final long serialVersionUID = 3863559687276427577L; private boolean success = true; public String message; private String secure;//安全凭据 private T data;//数据 public JsonResult() { } public JsonResult(T data, Boolean success, String message) { // TODO Auto-generated constructor stub this.data = data; this.success = success; this.message = message; } public boolean isSuccess() { return success; } public void setSuccess(boolean success) { this.success = success; } public T getData() { return data; } public void setData(T data) { this.data = data; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public static JsonResult newResult() { return new JsonResult (); } public String getSecure() { return secure; } public void setSecure(String secure) { this.secure = secure; }}
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定义一个视图接口,如下:public class GeneralViews { /** * use in error views */ public interface IErrorView{}; /** * use in success views */ public interface INormalView extends IErrorView{} ; }则JsonResult类改造为:
public class JsonResult最后,所用的业务VO改为:implements Serializable { private static final long serialVersionUID = 3863559687276427577L; @JsonView(GeneralViews.IErrorView.class) private boolean success = true; @JsonView(GeneralViews.IErrorView.class) public String message; @JsonView(GeneralViews.INormalView.class) private String secure;//安全凭据 @JsonView(GeneralViews.INormalView.class) private T data;//数据 public JsonResult() { } public JsonResult(T data, Boolean success, String message) { // TODO Auto-generated constructor stub this.data = data; this.success = success; this.message = message; } public boolean isSuccess() { return success; } public void setSuccess(boolean success) { this.success = success; } public T getData() { return data; } public void setData(T data) { this.data = data; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public static JsonResult newResult() { return new JsonResult (); } public String getSecure() { return secure; } public void setSecure(String secure) { this.secure = secure; } }
public final class Depot { public interface IOnlyIdView extends GeneralViews.INormalView{} public interface IOnlyNameView extends GeneralViews.INormalView{} @JsonView(IOnlyIdView.class) private long id; @JsonView(IOnlyNameView.class) private String name; private long num; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public long getNum() { return num; } public void setNum(long num) { this.num = num; } }再次运行Action,则结果为: {"success":true,"message":"ok","secure":null,"data":[{"name":"北京001仓"}]} 最终,通过这样的方式,成功实现了JsonView视图在泛型属性对象上的传递。
转载地址:http://mjpqi.baihongyu.com/