SpringBoot实现API接口的完整代码
一、简介
产品迭代过程中,同一个接口可能同时存在多个版本,不同版本的接口URL、参数相同,可能就是内部逻辑不同。尤其是在同一接口需要同时支持旧版本和新版本的情况下,比如APP发布新版本了,有的用户可能不选择升级,这是后接口的版本管理就十分必要了,根据APP的版本就可以提供不同版本的接口。
二、代码实现
本文的代码实现基于SpringBoot2.3.4-release
1.定义注解
ApiVersion
@Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public@interfaceApiVersion{ /** *版本。x.y.z格式 * *@return */ Stringvalue()default"1.0.0"; }
value值默认为1.0.0
EnableApiVersion
/** *是否开启API版本控制 */ @Target(ElementType.TYPE) @Documented @Retention(RetentionPolicy.RUNTIME) @Import(ApiAutoConfiguration.class) public@interfaceEnableApiVersion{ }
在启动类上添加这个注解后就可以开启接口的多版本支持。使用Import引入配置ApiAutoConfiguration。
2.将版本号抽象为ApiItem类
ApiItem
@Data publicclassApiItemimplementsComparable{ privateinthigh=1; privateintmid=0; privateintlow=0; publicstaticfinalApiItemAPI_ITEM_DEFAULT=ApiConverter.convert(ApiVersionConstant.DEFAULT_VERSION); publicApiItem(){ } @Override publicintcompareTo(ApiItemright){ if(this.getHigh()>right.getHigh()){ return1; }elseif(this.getHigh() right.getMid()){ return1; }elseif(this.getMid() right.getLow()){ return1; }elseif(this.getLow() 为了比较版本号的大小,实现Comparable接口并重写compareTo(),从高位到低位依次比较。
ApiConverter
publicclassApiConverter{ publicstaticApiItemconvert(Stringapi){ ApiItemapiItem=newApiItem(); if(StringUtils.isBlank(api)){ returnapiItem; } String[]cells=StringUtils.split(api,"."); apiItem.setHigh(Integer.parseInt(cells[0])); if(cells.length>1){ apiItem.setMid(Integer.parseInt(cells[1])); } if(cells.length>2){ apiItem.setLow(Integer.parseInt(cells[2])); } returnapiItem; } }ApiConverter提供静态方法将字符创转为ApiItem。
常量类,定义请求头及默认版本号
publicclassApiVersionConstant{ /** *header指定版本号请求头 */ publicstaticfinalStringAPI_VERSION="x-api-version"; /** *默认版本号 */ publicstaticfinalStringDEFAULT_VERSION="1.0.0"; }3.核心ApiCondition
新建ApiCondition类,实现RequestCondition,重写combine、getMatchingCondition、compareTo方法。
RequestCondition
publicinterfaceRequestCondition{ /** *方法和类上都存在相同的条件时的处理方法 */ Tcombine(Tother); /** *判断是否符合当前请求,返回null表示不符合 */ @Nullable TgetMatchingCondition(HttpServletRequestrequest); /** *如果存在多个符合条件的接口,则会根据这个来排序,然后用集合的第一个元素来处理 */ intcompareTo(Tother,HttpServletRequestrequest); 以上对RequestCondition简要说明,后续详细源码分析各个方法的作用。
ApiCondition
@Slf4j publicclassApiConditionimplementsRequestCondition{ publicstaticApiConditionempty=newApiCondition(ApiConverter.convert(ApiVersionConstant.DEFAULT_VERSION)); privateApiItemversion; privatebooleanNULL; publicApiCondition(ApiItemitem){ this.version=item; } publicApiCondition(ApiItemitem,booleanNULL){ this.version=item; this.NULL=NULL; } /** * *Spring先扫描方法再扫描类,然后调用{@link#combine} *按照方法上的注解优先级大于类上注解的原则处理,但是要注意如果方法上不定义注解的情况。 *如果方法或者类上不定义注解,我们会给一个默认的值{@codeempty},{@linkApiHandlerMapping} **@paramother方法扫描封装结果 *@return */ @Override publicApiConditioncombine(ApiConditionother){ //选择版本最大的接口 if(other.NULL){ returnthis; } returnother; } @Override publicApiConditiongetMatchingCondition(HttpServletRequestrequest){ if(CorsUtils.isPreFlightRequest(request)){ returnempty; } Stringversion=request.getHeader(ApiVersionConstant.API_VERSION); //获取所有小于等于版本的接口;如果前端不指定版本号,则默认请求1.0.0版本的接口 if(StringUtils.isBlank(version)){ log.warn("未指定版本,使用默认1.0.0版本。"); version=ApiVersionConstant.DEFAULT_VERSION; } ApiItemitem=ApiConverter.convert(version); if(item.compareTo(ApiItem.API_ITEM_DEFAULT)<0){ thrownewIllegalArgumentException(String.format("API版本[%s]错误,最低版本[%s]",version,ApiVersionConstant.DEFAULT_VERSION)); } if(item.compareTo(this.version)>=0){ returnthis; } returnnull; } @Override publicintcompareTo(ApiConditionother,HttpServletRequestrequest){ //获取到多个符合条件的接口后,会按照这个排序,然后get(0)获取最大版本对应的接口.自定义条件会最后比较 intcompare=other.version.compareTo(this.version); if(compare==0){ log.warn("RequestMappingInfo相同,请检查!version:{}",other.version); } returncompare; } }3.配置类注入容器
ApiHandlerMapping
publicclassApiHandlerMappingextendsRequestMappingHandlerMapping{ @Override protectedRequestCondition>getCustomTypeCondition(Class>handlerType){ returnbuildFrom(AnnotationUtils.findAnnotation(handlerType,ApiVersion.class)); } @Override protectedRequestCondition>getCustomMethodCondition(Methodmethod){ returnbuildFrom(AnnotationUtils.findAnnotation(method,ApiVersion.class)); } privateApiConditionbuildFrom(ApiVersionplatform){ returnplatform==null?getDefaultCondition(): newApiCondition(ApiConverter.convert(platform.value())); } privateApiConditiongetDefaultCondition(){ returnnewApiCondition(ApiConverter.convert(ApiVersionConstant.DEFAULT_VERSION),true); } }ApiAutoConfiguration
publicclassApiAutoConfigurationimplementsWebMvcRegistrations{ @Override publicRequestMappingHandlerMappinggetRequestMappingHandlerMapping(){ returnnewApiHandlerMapping(); } }ApiAutoConfiguration没有使用Configuration自动注入,而是使用Import带入,目的是可以在程序中选择性启用或者不启用版本控制。
三、原理解析
四、总结
到此这篇关于SpringBoot实现API接口的文章就介绍到这了,更多相关SpringBoot实现API接口内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!