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