• 自定义spring mvc的json视图


    场景

    我们团队现在面临着多端数据接口对接的问题,为了解决这个问题我们定义了接口对接的规范,
    前端(安卓,Ios,web前端)和后端进行了数据的格式规范的讨论,确定了json的数据格式:

    {
        "code":"200",
        "data":{"":""},
        "message":"处理成功"
    }
    {
        "code":"300",
        "data":{"":""},
        "message":"没有此用户"
    }
    

    code代表请求处理状态:200为正常处理,300为业务异常处理,500就系统异常处理。
    data代表后台返回的数据。
    message后台的提示语,正常或者成功的时候会返回错误原因。

    问题来了

    让每一个人对每一个json视图的返回值都要进行包装的话,岂不很麻烦,
    这个时候AOP就登场了,我们可以利用aop的思想在请求返回json之后还未response到客户端时为其包装上一层。

    实现步骤

    启用aop

        <!-- base-package 如果多个,用“,”分隔 -->
        <context:component-scan base-package="com.we,cn.isuyang">
            <context:include-filter type="annotation" expression="org.springframework.stereotype.Service" />
            <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
        </context:component-scan>
        <!-- 打开aop 注解 -->
       <aop:aspectj-autoproxy />
    

    创建切面

    /**
     * json返回切面
     * <p>
     * 用于处理json返回结果
     *
     * @author   ZhuangJunxiang(529272571@qq.com)
     * @Date     2017年4月28日      
     */
    @Component
    @Aspect
    @Order(2)
    public class JsonReturnAspect {
            
        /**
         * 设置分页默认值
         * <p>
         * 如果分页没有设置值,则默认从系统的配置文件里读取
         *
         * @param pjp 切点
        */
        @Around(value = "@annotation(org.springframework.web.bind.annotation.ResponseBody)")
        @Order(1)
        public Object warp(final ProceedingJoinPoint pjp) throws Throwable {
            Object list = pjp.proceed();
            if (isReturnVoid(pjp)) {
                HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                        .getResponse();
                if (isNeedWrap(pjp)) {
                    response.getWriter().write(JsonUtil.toJson(success("操作成功")));
                }
                return list;
            }
            return data(list);
        }
        /**
         * 是否需要包裹
         *
         * @param pjp 切点
         * 
         * @return true表示不需要
        */
        private boolean isNeedWrap(final ProceedingJoinPoint pjp) {
            Method method = AspectUtil.getMethod(pjp);
            return !method.isAnnotationPresent(Void.class);
        }
        /**
         * 是否返回空
         *
         * @param pjp
         * @return true:返回类型为void,false:返回类型不是void
        */
        private boolean isReturnVoid(ProceedingJoinPoint pjp) {
            Method method = AspectUtil.getMethod(pjp);
            Class<?> returnType = method.getReturnType();
            return "void".equals(returnType.getName());
        }  
    
        /**
         * 构建成功后的返回对象
         * <p>
         * 消息为空时,不提示,不为空则进行提示
         * 
         * @param message 成功消息
         * @return json对象
         */
        public static Map<String, Object> success(final String message) {
            Map<String, Object> map = MapUtil.map();
            map.put("code", StatusCode.SUCCESS.key());
            map.put("message", message);
            map.put("data","");
            return map;
        }        
        /**
         * 构建成功后的返回对象
         * <p>
         * 消息为空时,不提示,不为空则进行提示
         * 
         * @param message 成功消息
         * @return json对象
         */
        public static Map<String, Object> data(final Object data) {
            Map<String, Object> map = MapUtil.map();
            map.put("code", StatusCode.SUCCESS.key());
            map.put("message", message);
            map.put("data",data);
            return map;
        }        
    }
    

    分析一下

    @Component 这个注解表示将这个对象交给spring容器进行实例化

    @Aspect 表示这是一个切面类

    @Around(value = "@annotation(org.springframework.web.bind.annotation.ResponseBody)")

    表示凡是方法上带有@ResponseBody注解的都是这个切面中切点,换句话说都会被拦截。

    注意:
    warp方法中的ProceedingJoinPoint参数只有环绕通知才可以使用JoinPoint的子类ProceedingJoinPoint,
    各连接点类型可以调用代理的方法,并获取、改变返回值。否则就是用JoinPoint。

    情况一:假设conroller类中的函数不需要任何返回值

    比如:我对一个实体对象进行更新我只需要把更新结果返回去就OK了,不需要填充数据
    返回的数据格式:
    {
    "code":"200",
    "data":"",
    "message":"处理成功"
    }
    实现思路:
    在切面处理类的处理函数中获取到这个函数的返回值类型如果是void就返回指定格式的数据。
    上面的isReturnVoid()就是做这样的一个判断。

    你只需要将函数的返回值为void即可:

    @RequestMapping
    @ResponseBody
    public void add(long matchId, Model model) {
        slxSignupViewService.setAddInfo(matchId, model);
    }
    

    情况二:假设conroller类中的函数的返回值不需要包裹呢

    比如:
    某些前端插件以及第三方对接(支付)的返回值是规定好的,
    以及下载文件,我们这些就是多余了,
    实现思路:
    自定一个@Void的注解:

    /**
     * 空注解
     * <p>
     * 用于标识将controller层中的返回值原模原样的out出去
     * 
     * @author   WangSen(wangsenhehe@126.com)
     * @Date     2017年8月17日      
     */
    @Target({ ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface Void {
    
    }
    

    在controller层的方法上添加这个注解

     /**
       * 支付完成
       */
    @Void
    @ResponseBody
    @RequestMapping
    public void payFinish() throws IOException {
            alipayViewService.payFinish();
    }
    

    在这个切面处理类上判断这个函数是否包含这个注解如果包含
    就不作处理,原模原样的返回出去。
    JsonReturnAspect类中的isNeedWrap()方法就是处理这个需求。

    参考资料

    http://blog.csdn.net/zx13525079024/article/details/51884234

  • 相关阅读:
    leetcode Simplify Path
    leetcode Evaluate Reverse Polish Notation
    leetcode Swap Nodes in Pairs
    leetcode MinStack
    leetcode length of the last word
    empty能否代替isset?
    thinkphp框架的路径
    PHP 反射类的简单使用!
    在windows下配置redis扩展
    phpmyadmin的windows下和linux下的安装。
  • 原文地址:https://www.cnblogs.com/wangsen/p/8031283.html
Copyright © 2020-2023  润新知