springMVC修改接口注册映射逻辑
最近的一个需求,在现有的系统上实现同一接口的多版本访问,便于以后的接口升级,客户端不改变请求地址,在参数中传递一个version字段指定访问哪个版本的接口。
因为现系统已经在运行,所以改造不能影响已在使用的接口,最开始的设想是建立一个新接口,然后用拦截器拦截请求,组装请求地址+版本号,重定向到新接口,如下:
像上面那样,建立一个cpct/param/v20的新接口,客户端请求cpct/param.action?version=v2.0时重定向成cpct/param/v20.action,想法是美好的,客户端ajax请求根本无法实现重定向。
就因为开始的想法是这样,后面就华丽丽地跑偏了,脑子里全想的是怎么在服务端实现接口重定向,走了一大截弯路(当然这种方式也是能实现多版本控制的)。
拦截重定向行不通,就试试从springMVC的RequestMappingHandlerMapping的接口注册和查询逻辑上入手,迂回实现上面的功能。在方法上加个注解标注版本
|
|
翻阅RequestMappingHandlerMapping源码,在扫描方法的RequestMapping注解时有一个很重要的方法getMappingForMethod,它将含有RequestMapping注解的方法信息获取到生成RequestMappingInfo对象,作为键放在一个Map中(这个Map是内部类MappingRegistry的一个属性mappingLookup)。
幸好getMappingForMethod方法是protected的,重写这个方法
这样,所有带有@ApiVersion注解的接口都换成了新地址。在客户端请求时,需要修改获取接口的逻辑。RequestMappingHandlerMapping类从AbstractHandlerMethodMapping类继承了一个重要的方法getHandlerInternal,这个方法就是根据请求信息获取对应的地址。原方法实现如下:
最开始尝试重写这个方法,但是方法内部做了线程同步而mappingRegistry这个属性私有无法访问。调试发现从request中获取请求信息是通过UrlPathHelper这个工具类的getLookupPathForRequest方法完成的。AbstractHandlerMethodMapping类有一个公共方法getUrlPathHelper获取UrlPathHelper实例。所以解决方式就是继承UrlPathHelper,重写getLookupPathForRequest方法。
|
|
在CpctRequestMappingHandlerMapping类中添加如上代码,为了以防万一,重写了所有给urlPathHelper设置属性的代码,同时调用父类的相关方法。
修改application-servlet.xml配置。如果有<mvc:annotation-driven>配置,需要注释掉,手动配置
在实际开发过程中这样做没有多少实际意义,springMVC本身扩展性比较强,大多数需求都可以通过扩展完成,但这也是一种思路,万一哪天遇到一些特殊的需求呢。
RequestMappingHandlerMapping源码解读可以参考:
SpringMVC源码解读 - HandlerMapping - RequestMappingHandlerMapping初始化
springMVC (十) RequestMappingHandlerMapping