APP的接口版本管理 实现多版本共存

4 篇文章 0 订阅
2 篇文章 0 订阅

思路:用户请求url ---> 拦截器拦截 ---> 转发到真正处理类和方法 ---> 返回结果

url注解类

package com.jc.app.util.apiVersion;

import java.lang.annotation.*;

/**
 * 需要拦截的API接口方法
 * Created by jasonzhu on 2016/11/28.
 */

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiVersion {
    /**
     * 指定执行操作的类名
     */
    Class targetClass();

    /**
     * 指定执行操作的方法名前缀
     */
    String methodPreName() default "";

}

执行处理方法参数注解类

package com.jc.app.util.apiVersion;

import java.lang.annotation.*;

/**
 * 处理方法的参数注解
 * Created by jasonzhu on 2016/11/30.
 */
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiParam {
    /**
     * 参数名
     */
    String value();

    /**
     * 是否必须有值 默认必须有
     */
    boolean required() default true;

    /**
     * 值非必须时 如果未传值 默认值
     */
    DefaultValueEnum defaultValue() default DefaultValueEnum.DEFAULT;
}

默认值枚举

package com.jc.app.util.apiVersion;

/**
 * 默认参数值
 * Created by jasonzhu on 2016/11/30.
 */
public enum DefaultValueEnum {
    /**
     * 按照类型赋默认值
     * String ""
     * Boolean false
     * Integer 0
     * Long 0
     * 其他 null
     */
    DEFAULT,
    NULL,
    STRING_EMPTY,
    ZERO,
    FALSE,
    TRUE
}

异常

package com.jc.app.util.apiVersion;

/**
 * 版本控制异常
 * Created by jasonzhu on 2016/11/30.
 */
public class ApiVersionException extends RuntimeException {

    public ApiVersionException(String message) {
        super(message);
    }

    public ApiVersionException(String message, Throwable cause) {
        super(message, cause);
    }
}

版本拦截器

package com.jc.app.util.apiVersion;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.Map;

/**
 * 接口版本拦截器
 * Created by jasonzhu on 2016/11/28.
 */
public class ApiVersionInterceptor extends HandlerInterceptorAdapter {
    /**
     * 接口版本参数名
     */
    final String API_VERSION = "av";
    final String STRING_DEFAULT = "";
    final Integer INTEGER_DEFAULT = 0;
    final Long LONG_DEFAULT = 0L;
    final Boolean BOOLEAN_DEFAULT = false;

    @Autowired
    private ApplicationContext context;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod method = (HandlerMethod) handler;
        ApiVersion apiVersion = method.getMethodAnnotation(ApiVersion.class);
        //判断是否纳入接口版本控制
        if (apiVersion == null) {
            return true;
        }

        Class cls = apiVersion.targetClass();
        Object o;
        try {
            o = context.getBean(cls);
        } catch (Exception e) {
            throw new ApiVersionException("指定的处理类必须纳入spring的bean管理", e);
        }
        String preName = apiVersion.methodPreName();
        if (preName == null || preName.trim().isEmpty()) {
            preName = method.getMethod().getName();
        }
        //接口版本号
        String av = "1";
        //参数列表
        Map<String, String[]> requestParam = request.getParameterMap();
        if (requestParam.get(API_VERSION) != null) {
            av = requestParam.get(API_VERSION)[0];
        }
        Method[] methods = cls.getMethods();
        if (methods == null) {
            writeMsg(response, "未找到响应方法");
            return false;
        }
        Method targetMethod = null;
        //找到指定的处理方法
        for (Method me : methods) {
            if (me.getName().equals(preName + av)) {
                targetMethod = me;
                break;
            }
        }
        if (targetMethod == null) {
            writeMsg(response, "非法请求");
            return false;
        }
        if (!targetMethod.getReturnType().equals(String.class)) {
            throw new ApiVersionException("响应方法返回类型必须为String :" + targetMethod.getName());
        }
        //获得方法的参数
        Class<?>[] paramTypes = targetMethod.getParameterTypes();
        Integer paramLength = paramTypes.length;

        //调动方法的参数
        Object[] paramList = new Object[paramLength];
        Annotation[][] annotationss = targetMethod.getParameterAnnotations();
        //总注解参数个数
        for (int i = 0; i < annotationss.length; i++) {
            Annotation[] annotations = annotationss[i];
            if (annotations.length < 1)
                throw new ApiVersionException("存在未添加@ApiParam注解参数的方法 :" + targetMethod.getName());
            //是否存在ApiParam注解
            boolean hasAnn = false;
            for (int j = 0; j < annotations.length; j++) {
                Annotation annotation = annotations[j];
                if (annotation instanceof ApiParam) {
                    //为参数赋值
                    paramList[i] = getParam(requestParam, (ApiParam) annotation, paramTypes[i]);
                    hasAnn = true;
                    break;
                }
            }
            if (!hasAnn)
                throw new ApiVersionException("存在未添加@ApiParam注解参数的方法 :" + targetMethod.getName());
        }

        //反射方法调用
        String result = (String) targetMethod.invoke(o, paramList);
        writeMsg(response, result);
        return false;
    }

    /**
     * 输出内容
     *
     * @param response
     * @param msg
     * @throws Exception
     */
    private void writeMsg(HttpServletResponse response, String msg) throws Exception {
        response.setContentType("application/json;charset=UTF-8");
        PrintWriter out = response.getWriter();
        out.println(msg);
        out.flush();
        out.close();
    }

    /**
     * 获得参数的值
     *
     * @param requestParam 请求参数
     * @param apiParam     参数上注解
     * @param paramType    参数类型
     * @return 参数值
     */
    private Object getParam(Map<String, String[]> requestParam, ApiParam apiParam, Class<?> paramType) {
        String reqName = apiParam.value();
        //如果有该参数
        if (requestParam.get(reqName) != null) {
            Object o = requestParam.get(reqName)[0];
            try {
                if (paramType.equals(String.class)) {
                    return String.valueOf(o);
                }
                if (paramType.equals(Boolean.class) || "boolean".equals(paramType.getName())) {
                    return Boolean.parseBoolean(o.toString());
                }
                if (paramType.equals(Integer.class) || "int".equals(paramType.getName())) {
                    return Integer.parseInt(o.toString());
                }
                if (paramType.equals(Long.class) || "long".equals(paramType.getName())) {
                    return Long.parseLong(o.toString());
                }
                if (paramType.equals(BigDecimal.class)) {
                    return new BigDecimal(o.toString());
                }
            } catch (Exception e) {
                throw new ApiVersionException("参数格式化失败 :" + reqName, e);
            }
            return o;
        }
        //如果参数必须
        if (apiParam.required()) {
            throw new ApiVersionException("缺少参数 :" + reqName);
        }
        //返回默认值
        DefaultValueEnum dfe = apiParam.defaultValue();
        if (DefaultValueEnum.DEFAULT.equals(dfe)) {
            if (paramType.equals(String.class)) {
                return STRING_DEFAULT;
            }
            if (paramType.equals(Boolean.class) || "boolean".equals(paramType.getName())) {
                return BOOLEAN_DEFAULT;
            }
            if (paramType.equals(Integer.class) || "int".equals(paramType.getName())) {
                return INTEGER_DEFAULT;
            }
            if (paramType.equals(Long.class) || "long".equals(paramType.getName())) {
                return LONG_DEFAULT;
            }
        } else if (DefaultValueEnum.NULL.equals(dfe)) {
            return null;
        } else if (DefaultValueEnum.STRING_EMPTY.equals(dfe)) {
            return "";
        } else if (DefaultValueEnum.ZERO.equals(dfe)) {
            return 0;
        } else if (DefaultValueEnum.FALSE.equals(dfe)) {
            return false;
        } else if (DefaultValueEnum.TRUE.equals(dfe)) {
            return true;
        }
        return null;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }
}

spring mvc中增加的配置
<!-- 版本控制拦截器 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**" />
            <bean class="com.jc.app.util.apiVersion.ApiVersionInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>
测试类

controller

package com.jc.app.controller;

import com.jc.app.util.apiVersion.ApiVersion;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 测试版本控制接口
 * Created by jasonzhu on 2016/12/1.
 */
@RestController
public class TestApiVersionController {
    @ApiVersion(targetClass = TestApiVersionDo.class)
    @RequestMapping("/api/test")
    public void test(){}
    @ApiVersion(targetClass = TestApiVersionDo.class,methodPreName = "test")
    @RequestMapping("/api/testno")
    public void testNo(){}
}

真正执行方法的类

package com.jc.app.controller;

import com.jc.app.util.apiVersion.ApiParam;
import com.jc.app.util.apiVersion.DefaultValueEnum;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;

/**
 * 版本拦截器之后真正执行的方法
 * Created by jasonzhu on 2016/12/1.
 */
@Component
public class TestApiVersionDo {
    public void test1(){}
    private String test2(){return "调用成功 没有参数";}
    public String test3(){
        return "调用成功 没有参数";
    }
    public String test4(@ApiParam("a") String app){
        return "调用成功 一个参数 app:"+app;
    }
    public String test5(@ApiParam("av") Integer av,@ApiParam("a") String app,@ApiParam(value = "b",required = false) String b){
        return "调用成功 三个参数 app:"+app+" av:"+av+" b:"+b;
    }
    public String test6(@ApiParam("amount") BigDecimal amount, @ApiParam(value = "l",required = false) long l, @ApiParam(value = "b",required = false,defaultValue = DefaultValueEnum.TRUE) Boolean b){
        return "调用成功 三个参数 amount:"+amount+" l:"+l+" b:"+b;
    }
}


  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值