zoukankan      html  css  js  c++  java
  • java spring使用Jackson过滤

    一、问题的提出。 

    项目使用Spring MVC框架,并用jackson库处理JSON和POJO的转换。在POJO转化成JSON时,希望动态的过滤掉对象的某些属性。所谓动态,是指的运行时,不同的controler方法可以针对同一POJO过滤掉不同的属性。 

    以下是一个Controler方法的定义,使用@ResponseBody把获得的对象列表写入响应的输出流(当然,必须配置jackson的MappingJacksonHttpMessageConverter,来完成对象的序列化)

    1
    2
    3
    4
    5
    6
    7
    8
    @RequestMapping(params = "method=getAllBmForList")
    @ResponseBody
    public List<DepartGenInfo> getAllBmForList(HttpServletRequest request,
            HttpServletResponse response) throws Exception {
         
        BmDto dto = bmglService.getAllBm();
        return dto.getBmList();
    }

    POJO定义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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动态过滤

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @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的定义如下:

    @JsonIgnoreProperties(value={"name"}) //希望动态过滤掉的属性
    public interface DepartGenInfoFilter {
    }//CompanyFilter的定义如下: 
    serializationConfig.addMixInAnnotations();  

    这个实现方法看起来非常不简洁,需要在动态过滤的时候写不少代码,而且也改变了@ResponseBody的运行方式,失去了REST风格,因此考虑到使用AOP来进行处理。 

    二、最终解决方案 

    先看下我想达到的目标,通过自定义注解的方式来控制动态过滤。

    @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定义了混合的模板以及目标类。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @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如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    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;
        }
     
    }

    其中pointcut的expression能够匹配到目标类的方法。 

    在doAround方法中,需要获得当前引用的HttpResponse对象,因此使用以下方法解决: 

    创建一个WebContext工具类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    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);
        }
    }

    别忘了在web.xml中增加这个filter。 




    OK,It is all。 




    四、总结 

    设计的一些要点: 

    1、要便于程序员使用。程序员根据业务逻辑需要过滤字段时,只需要定义个"Filter“,然后使用注解引入该Filter。 

    2、引入AOP来保持原来的REST风格。对于项目遗留的代码,不需要进行大幅度的修改,只需要增加注解来增加对过滤字段的支持。 

    仍需解决的问题: 

    按照目前的设计,定义的Filter不支持继承,每一种动态字段的业务需求就会产生一个Filter类,当类数量很多时,不便于管理。 




    五、参考资料 

    http://www.cowtowncoder.com/blog/archives/cat_json.html 

    http://www.jroller.com/RickHigh/entry/filtering_json_feeds_from_spring

    原文地址:https://www.cnblogs.com/zcw-ios/articles/3343071.html
  • 相关阅读:
    穿越之我是码农 1024 篇
    误删文件机房停电黑客入侵_你最怕什么?
    AI觉醒进行时:程序员你怕了吗?
    未来已来!阿里小蜜AI技术揭秘
    千人千面智能淘宝店铺背后的算法研究登陆人工智能顶级会议AAAI 2017
    CDN缓存不命中排查
    现实需求巨大_技术尚未成熟_学界与业界思维大碰撞
    围观阿里云最会赚钱的人!价值2万元邀请码不限量发送
    今晚19:30直播阿里巴巴大规模持续集成的技术演进之路_欢迎免费观看
    工作压力山大?码农这么减压最有效
  • 原文地址:https://www.cnblogs.com/jpfss/p/11063039.html
Copyright © 2011-2022 走看看