Spring MVC中使用jackson的MixInAnnotations方法动态过滤JSON字段
2015-06-10 14:17
597 查看
一、问题的提出。 项目使用Spring MVC框架,并用jackson库处理JSON和POJO的转换。在POJO转化成JSON时,希望动态的过滤掉对象的某些属性。所谓动态,是指的运行时,不同的controler方法可以针对同一POJO过滤掉不同的属性。 以下是一个Controler方法的定义,使用@ResponseBody把获得的对象列表写入响应的输出流(当然,必须配置jackson的MappingJacksonHttpMessageConverter,来完成对象的序列化) [java] view plaincopy @RequestMapping(params = "method=getAllBmForList") @ResponseBody public List<DepartGenInfo> getAllBmForList(HttpServletRequest request, HttpServletResponse response) throws Exception { BmDto dto = bmglService.getAllBm(); return dto.getBmList(); } POJO定义如下 [java] view plaincopy public class DepartGenInfo implements java.io.Serializable { private String depid; private String name; private Company company; //getter... //setter... } public class Company { private String comid; private String name; <pre name="code" class="java"> //getter... //setter... } 我希望在getAllBmForList返回时,过滤掉DepartGenInfo的name属性,以及company的comid属性。 jackson支持@JsonIgnore和@JsonIgnoreProperties注解,但是无法实现动态过滤。jackson给出了几种动态过滤的办法,我选择使用annotation mixin JSON View JSON Filter Annotation Mixin 二、使用annotation mixin动态过滤 [java] view plaincopy @RequestMapping(params = "method=getAllBmForList") public void getAllBmForList(HttpServletRequest request, HttpServletResponse response) throws Exception { BmDto dto = bmglService.getAllBm(); ObjectMapper mapper = new ObjectMapper(); SerializationConfig serializationConfig = mapper.getSerializationConfig(); serializationConfig.addMixInAnnotations(DepartGenInfo.class, DepartGenInfoFilter.class); serializationConfig.addMixInAnnotations(Company.class, CompanyFilter.class); mapper.writeValue(response.getOutputStream(),dto.getBmList()); return; } DepartGenInfoFilter的定义如下: [java] view plaincopy @JsonIgnoreProperties(value={"name"}) //希望动态过滤掉的属性 public interface DepartGenInfoFilter { } CompanyFilter的定义如下: [java] view plaincopy @JsonIgnoreProperties(value={"comid"}) //希望动态过滤掉的属性 public interface CompanyFilter{ } 这样处理便能够动态过滤属性。如果需要修改过滤的属性,只需要定义新的一个"Filter”,然后使用 [java] view plaincopy serializationConfig.addMixInAnnotations(); 这个实现方法看起来非常不简洁,需要在动态过滤的时候写不少代码,而且也改变了@ResponseBody的运行方式,失去了REST风格,因此考虑到使用AOP来进行处理。 二、最终解决方案 先看下我想达到的目标,通过自定义注解的方式来控制动态过滤。 [java] view plaincopy @XunerJsonFilters(value={@XunerJsonFilter(mixin=DepartGenInfoFilter.class, target=DepartGenInfo.class) ,@XunerJsonFilter(mixin=CompanyFilter.class, target=Company.class)}) @RequestMapping(params = "method=getAllBmForList") @ResponseBody public List getAllBmForList(HttpServletRequest request, HttpServletResponse response) throws Exception { BmDto dto = bmglService.getAllBm(); return dto.getBmList(); } @XunerJsonFilters和@XunerJsonFilter是我定义的注解。@XunerJsonFilters是@XunerJsonFilter的集合,@XunerJsonFilter定义了混合的模板以及目标类。 [java] view plaincopy @Retention(RetentionPolicy.RUNTIME) public @interface XunerJsonFilters { XunerJsonFilter[] value(); } @Retention(RetentionPolicy.RUNTIME) public @interface XunerJsonFilter { Class<?> mixin() default Object.class; Class<?> target() default Object.class; } 当然,只是定义注解并没有什么意义。重要的是如何根据自定义的注解进行处理。我定义了一个AOP Advice如下: [java] view plaincopy public class XunerJsonFilterAdvice { public Object doAround(ProceedingJoinPoint pjp) throws Throwable { MethodSignature msig = (MethodSignature) pjp.getSignature(); XunerJsonFilter annotation = msig.getMethod().getAnnotation( XunerJsonFilter.class); XunerJsonFilters annotations = msig.getMethod().getAnnotation( XunerJsonFilters.class); if (annotation == null && annotations == null) { return pjp.proceed(); } ObjectMapper mapper = new ObjectMapper(); if (annotation != null) { Class<?> mixin = annotation.mixin(); Class<?> target = annotation.target(); if (target != null) { mapper.getSerializationConfig().addMixInAnnotations(target, mixin); } else { mapper.getSerializationConfig().addMixInAnnotations( msig.getMethod().getReturnType(), mixin); } } if (annotations != null) { XunerJsonFilter[] filters= annotations.value(); for(XunerJsonFilter filter :filters){ Class<?> mixin = filter.mixin(); Class<?> target = filter.target(); if (target != null) { mapper.getSerializationConfig().addMixInAnnotations(target, mixin); } else { mapper.getSerializationConfig().addMixInAnnotations( msig.getMethod().getReturnType(), mixin); } } } try { mapper.writeValue(WebContext.getInstance().getResponse() .getOutputStream(), pjp.proceed()); } catch (Exception ex) { throw new RuntimeException(ex); } return null; } } 在Spring MVC中进行AOP的配置 [html] view plaincopy <bean id="xunerJsonFilterAdvice" class="com.xunersoft.common.json.XunerJsonFilterAdvice"/> <aop:config> <aop:aspect id="jsonFilterAspect" ref="xunerJsonFilterAdvice"> <aop:pointcut id="jsonFilterPointcut" expression="execution(* com.xunersoft.webapp.rsgl.controller.*.*(..))"/> <aop:around pointcut-ref="jsonFilterPointcut" method="doAround"/> </aop:aspect> </aop:config> 其中pointcut的expression能够匹配到目标类的方法。 在doAround方法中,需要获得当前引用的HttpResponse对象,因此使用以下方法解决: 创建一个WebContext工具类: [java] view plaincopy public class WebContext { private static ThreadLocal<WebContext> tlv = new ThreadLocal<WebContext>(); private HttpServletRequest request; private HttpServletResponse response; private ServletContext servletContext; protected WebContext() { } public HttpServletRequest getRequest() { return request; } public void setRequest(HttpServletRequest request) { this.request = request; } public HttpServletResponse getResponse() { return response; } public void setResponse(HttpServletResponse response) { this.response = response; } public ServletContext getServletContext() { return servletContext; } public void setServletContext(ServletContext servletContext) { this.servletContext = servletContext; } private WebContext(HttpServletRequest request, HttpServletResponse response, ServletContext servletContext) { this.request = request; this.response = response; this.servletContext = servletContext; } public static WebContext getInstance() { return tlv.get(); } public static void create(HttpServletRequest request, HttpServletResponse response, ServletContext servletContext) { WebContext wc = new WebContext(request, response, servletContext); tlv.set(wc); } public static void clear() { tlv.set(null); } } 定义一个Servlet Filter: [java] view plaincopy @Component("webContextFilter") public class WebContextFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) resp; ServletContext servletContext = request.getSession().getServletContext(); WebContext.create(request, response, servletContext); chain.doFilter(request, response); WebContext.clear(); } @Override public void destroy() { // TODO Auto-generated method stub } } 别忘了在web.xml中增加这个filter。 OK,It is all。 四、总结 设计的一些要点: 1、要便于程序员使用。程序员根据业务逻辑需要过滤字段时,只需要定义个"Filter“,然后使用注解引入该Filter。 2、引入AOP来保持原来的REST风格。对于项目遗留的代码,不需要进行大幅度的修改,只需要增加注解来增加对过滤字段的支持。 仍需解决的问题: 按照目前的设计,定义的Filter不支持继承,每一种动态字段的业务需求就会产生一个Filter类,当类数量很多时,不便于管理。 |
相关文章推荐
- JavaBean编程的基本思路-逻辑业务层
- java设计模式 ----装饰者模式
- struts2.2.3 + spring3.1.0 + mybatis3.1.0集成简单demo
- 怎样分析 JAVA 的 Thread Dumps
- 彻底解决Spring MVC 中文乱码 问题
- 彻底解决Spring MVC 中文乱码 问题
- java获取客户端IP地址
- Eclipse修改快捷键
- gradle多渠道打包及友盟统计-eclipse版本
- JAVA基础--集合
- Java NIO:NIO概述
- 详解java定时任务
- eclipse jetty debug
- 深入研究Java类加载机制
- 为MyEclipse添加自定义注释
- Spring小结
- struts.xml配置详解
- Java NIO原理 图文分析及代码实现
- JMeter java.lang.OutOfMemoryError: PermGen space错误
- Spring学习笔记之jdbc模版