1、背景:

  公司基础设施部门推出了自己的微服务框架(以下简称M),要求所有业务应用都要接入进去,但坑爹的是M只提供了SpringMVC工程的support,对于采用Stripes作为MVC框架的应用并不支持,所以就必须重构这个Stripes应用。

  M虽然代替Tomcat自己实现了IO,工作线程池,服务注册发现等等,但还是提供了对Servlet规范的简单支持(移除了web.xml,filter,listener, 打包方式也改成了tar.gz,保留了ServletContext,request,response等等)。

2、思路与分析:

  由于现有应用的业务逻辑还是比较复杂(沉淀了3,4年),且涉及到多个APP端和其他第三方的交互,所以如果想对应用做完全的重构是一件非常头痛的事情,不仅要投入大量的后端,测试等人力成本,而且整个重构周期也会比较长,影响到对其他需求的响应。

  于是在想:既然SpringMVC和Stripes都遵循Servlet规范,而M又能支持SpringMVC,是否可以通过SpringMVC拦截到所有请求后,把之前的老接口的请求转发给Stripes处理,以后新开发的接口就直接走SpringMVC,这样既兼容了公司的M框架也对原有应用的改动较小,开发周期短,测试人员只需要验证一些系统边界和主要流程,不用再挨个挨个验证已有的业务逻辑,这样可以节省大量人力成本,缩短开发周期。

  SpringMVC是目前最流行的MVC框架,是Spring庞大家族的一员,而Stripes也是一个非常轻量的MVC框架,但是SpringMVC的Controller跟Stripes的ActionBean有个很明显的区别就是前者是单例的,跟状态无关,所有的请求参数都是封装在ServletRequest中的,而后者是prototype,每次请求都会重新生成一个ActionBean对象。

  Spring有很强的可扩展性,SpringMVC也不例外,下面简单列了一下扩展的几个思路:

  1.扩展mvc名称空间:扩展RequestMappingHandlerAdapter等
    优点是:可以将整个请求的处理流程纳入到SpringMVC
    缺点是:侵入性较大,SpringMVC版本升级后需要再做适配,技术实现比较麻烦

  2.扩展DispatcherServlet:重写onRefresh或者doService等
         M的spring-support是直接创建DispatcherServlet的,需要M的同事开回调入口,不太现实

  3.增加拦截器,没有侵入性,不影响后续SpringMVC升级版本等
      缺点是扩展性不是很好,不过对桥接需求来说已经完全足够

  4.自定义Handlermapping,没有侵入性,理论上可以保证现有拦截器不需要改造,
        但是会导致SpringMVC的拦截器和Stripes的拦截器定义两套,后期维护麻烦(两个地方都需要修改)

       经过对比和讨论,我选择了第三点,新增一个SpringMVC拦截器,下图是一个简单的流程示意图:

3、动手实现原型:

新建桥接工程XXX-bridge,所有新增代码均放在新工程,对老工程零侵入(老板特别强调,如果桥接失败了,一定不能影响现在核心工程,要能马上回退回去),所以老工程仅新增了pom文件里的两个plugin,可以实现Stripes和SpringMVC两种框架模式下的并行开发,(原来的老工程持续有需求进来)。

    /**
* 拦截所有请求,如果能映射到Stripes则直接派发给Stripes并return false,如果不能映射则回退给SpringMVC
*/
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
if (handlerMapping.isEmpty()) {
return true;
}
String url = httpServletRequest.getRequestURI();
String contextPath = httpServletRequest.getContextPath();
url = url.replace(contextPath, "").replaceAll("/+", "/");
LOGGER.info("当前请求路径: {}", url);
if (!StripesContextHandlerInterceptor.handlerMapping.containsKey(url)) {
LOGGER.info("请求由SpringMVC处理: {}", url);
return true;
}
try {
LOGGER.info("请求由Stripes处理: {}", url);
StripesContextHolder.doService(httpServletRequest.getServletContext(), getApplicationContext(),
httpServletRequest, httpServletResponse);
return false;
} catch (Exception e) {
LOGGER.error("处理请求异常:", e);
return false;
}
}

关键点在于要解析到所有的老接口地址,然后通过拦截器来判断当前请求是否是老接口,是的话就转发给Stripes来处理,其他详细的代码托管在github,欢迎访问。

https://github.com/hiccup234/web-advance/tree/master/bridge

4、问题与验证:

1、因为初始化Stripes需要 ServletContext,但是应用启动时却没办法获取到 ServletContext 对象,所以在 StripesContextHolder 里做了懒加载,只有当第一个请求进来后才加载和初始化Stripes框架。

  2、Stripes是可以支持queryString为空的查询的,类似:http://127.0.0.1/server/test?list  而M会在解析请求参数的时候对这种情况直接抛异常,经过与基础设施同事协商,他们做出让步,对这种请求的情况直接放行给应用,从而避免了前端和第三方做改造。

  今晚上线成功,目前跟Tomcat并行发布,5%的流量灰度,观察日志一切正常。前期的技术验证加上近两周的开发联调到今天的上线,算是向着架构的方向迈出了小小一步(同时:也感谢测试同学的自动化,让验证和回归省去了很多工作量)

拯救老旧工程,记桥接SpringMVC与Stripes框架的更多相关文章

  1. 老的工程移植到AndroidStudio需要修改的注意事项

    之前老的工程用android-apt编译,如果要在新的AndroidStudio编译至少需要修改一下几部分: 1. 修改project里的build.gradle dependencies { cla ...

  2. HomeKit 与老旧设备

    苹果推了HomeKit,已经有很多厂商在做,可以达到Siri控制所有设备的功能. 但是Siri也不是万能的,对人类的语义理解也会产生差错,不过我相信未来这个问题会解决掉.     如果家里有老旧的电视 ...

  3. 7款适用老旧设备并对初学者非常友好的轻量级Linux发行版

    我们由从 7 到 1 的顺序向大家介绍. 7. Linux Lite 正如其名,Linux Lite 是 Linux 发行版的一个轻量级版本,用户并不需要强大的硬件就可以将它跑起来,而且其使用非常简单 ...

  4. Vivado2016旧工程IP移动到新工程

    前言 在VIVADO中图形化界面生成IP,在旧工程中使用后,如果想在新的工程中使用这个IP但又不想再次生成一次,则就需要把就旧工程中的IP导入到新工程中. 流程 1.旧工程IP生成的目录如下,IP为C ...

  5. Angular 学习笔记 ( 创建 library, 转换老旧的 library )

    更新 : 2018-10-28 不知道为什么在 ng 跑一直做不到 .d.ts 最后发现,如果有一个插件 propagating-hammerjs.ts 那么就在 root create 一个 pro ...

  6. 用mongodb 固定集合实现只保留固定数量的记录,自动淘汰老旧数据

    在一个保存report记录的场景中,我们使用MongoDB进行数据存储 example: db: report Collection: daily_report 创建db:  use report; ...

  7. CentOS7清理老旧内核

    CentOS7如果是一步步内核升级上来的将会面临一个问题(一般安装了图形化界面的系统常见),由于内核增多/boot/的容量减少,导致启动变慢.这时就需要清理老旧内核,释放/boot/空间. 使用una ...

  8. 手把手Maven搭建SpringMVC+Spring+MyBatis框架(超级详细版)

    手把手Maven搭建SpringMVC+Spring+MyBatis框架(超级详细版) SSM(Spring+SpringMVC+Mybatis),目前较为主流的企业级架构方案.标准的MVC设计模式, ...

  9. Springmvc整合tiles框架简单入门示例(maven)

    Springmvc整合tiles框架简单入门示例(maven) 本教程基于Springmvc,spring mvc和maven怎么弄就不具体说了,这边就只简单说tiles框架的整合. 先贴上源码(免积 ...

随机推荐

  1. [VUE]object.defineProperty的基本使用

    1.object.defineProperty 给一个对象定义一个新的属性或者在修改一个对象现有的属性,并返回这个对象 语法: Object.defineProperty(参数1,参数2,参数3) 参 ...

  2. js与jq基础记录

    1.在js传递参数中含加号(+)的处理方式: 只需要把js中传过去的+号替换成base64 编码 %2B:encodeURI(str).replace(/\+/g,'%2B'). 2.随机产生8位随机 ...

  3. JS DOM与BOM

    DOM知识点 [DOM(文档对象模型)是 HTML 和 XML 的应用程序接口(API).DOM代表着被加载到浏览器窗口里的当前网页:浏览器向我们提供了当前网页的地图(或者说模型),而我们可以通过js ...

  4. Deployment Characteristics of "The Edge" in Mobile Edge Computing

    移动边缘计算中的“边缘”部署特性 本文为SIGCOMM 2018 Workshop (Mobile Edge Communications, MECOMM)论文. 本文翻译了论文的关键内容. 摘要 移 ...

  5. [Swift]LeetCode248.对称数 III $ Strobogrammatic Number III

    A strobogrammatic number is a number that looks the same when rotated 180 degrees (looked at upside ...

  6. [Swift]LeetCode409. 最长回文串 | Longest Palindrome

    Given a string which consists of lowercase or uppercase letters, find the length of the longest pali ...

  7. [Swift]LeetCode736. Lisp 语法解析 | Parse Lisp Expression

    You are given a string expressionrepresenting a Lisp-like expression to return the integer value of. ...

  8. [Swift]LeetCode979. 在二叉树中分配硬币 | Distribute Coins in Binary Tree

    Given the root of a binary tree with N nodes, each node in the tree has node.val coins, and there ar ...

  9. Python、pip和scrapy的安装——Python爬虫学习笔记1

    Python作为爬虫语言非常受欢迎,近期项目需要,很是学习了一番Python,在此记录学习过程:首先因为是初学,而且当时要求很快速的出demo,所以首先想到的是框架,一番查找选用了Python界大名鼎 ...

  10. WeakHashMap

    WeakHashMap 今天在具体业务的时候看到HashMap和WeakHashMap的区别.因为PHP语言并没有这种概念.所以很好奇做了一下研究. WeakHashMap WeakHashMap所谓 ...