用过 Turbine 之后老码农对 Web 做事框架的观点逐渐开始建立起来, 然而 MVC 模式和 Web 做事框架之间的关系之后后来 Spring 出了 SpringMVC 之后才更加清晰地定义下来并为业界接管. 遗憾地是 2003 年之后老码农转向嵌入式系统和 Java web 做事这条线暂时分道扬镳, 错过了这些年这个领域许多的精彩. 2009 年迈码农重回 Web 开拓, 先和 CakePHP 搏斗三个月, 创造自己实在不是宇宙第一措辞的对手, 决定还是回去找原配 Java. 当时 SSH 在 Java Web 做事框架之中已经如日中天, 但老码农并没有直接投怀入抱(当然更没有想过去踩 EJB 的深坑), 还是自持地决定再研究研究其他框架. 这以自持就让老码农碰上了真爱 PlayFramework. 和本日的 Play2 不同, 当年的 Play 给人一种惊艳的觉得, 最激动民气的几个特性:
开拓模式热加载 - 修正缺点之后直接在浏览器上点 reload 就 okay - 无需重启做事! 无需重启做事! 无需重启做事!开拓模式发生缺点直接在 Web 页面高亮显示缺点代码行 - 没有用过这个特性的开拓估计很难有直不雅观的观点, 这里是一个 play 的出错页面, 大家可以体会一下:无状态模型 - Session 不在做事端, 做横向扩展毫无违和感读到这里, 有的看官可能会说这几个特貌似和代码没有啥关系啊. 这里实在是老码农想强调的地方, 框架并非仅仅关乎代码, 而是关乎全体开拓过程. 设想一下如果每次修正无需等待 10 秒或者更长的韶光让做事重启, 全体开拓会节省多少小时; 同理, 如果出错之后无需到后台花几十秒到几分钟去 Log 中定位缺点, 又能够为全体项目节省多少小时. 老码农认为 Play1 带来的这些特性对生产力的提高是有革命性的影响.
光阴飞逝如电, 转眼进入二十世纪第二个十年. 如果说二十世纪的第一个十年奠定了 Java Web 做事框架的根本, 那第二个十年便是 Java Web 做事框架的井喷. TechEmpower 最近一期的 Java Micro/全栈 Web 做事框架 有三十种之多. 而海内的 JFinal, Nutz, RedKale, tio-mvc 等各种框架也是诸雄并起, 全体行业一遍欣欣向荣(春秋战国)景象.
2. \"大众轻量\"大众 的定(歧)义曾几何时, \"大众轻量\公众 二字浮出水面. 溯其根源, 大致和当年的 EJB 有关. 话说老码农一贯是 Java 的虔诚拥泵, 然而划线止于 J2EE, 其带来的 EJB 各种观点与操作都让老码农感到十分别扭. 后续有人大胆离经叛道, 声明 J2EE 太重, 业界须要一个更轻的框架, 于是乎 SpringFramework 应运而生, 可以说 \"大众轻量\"大众 这个定语起先原是润色 Spring 的. 然而花着花落去, 10 年光阴在 IT 上也算得上沧海桑田, 昔日翩翩少年郎 Spring 如今也成了中年油腻大叔, 貌似和 \公众轻量\"大众 早说再见, 而反被贴上了 \"大众重量\公众 的标签. \"大众以彼之道还施彼身\公众, 当下浩瀚框架(或其他工具软件)彷佛都深谙苏州慕容的绝技, 纷纭成为新一代的 \公众轻量\"大众 产品, 仿佛如不加 \公众轻量\公众 二字就不得人欢畅. 各位看官如不相信且请点击此处, 亦或有其他更为高明表达办法, 诸如 \"大众Tiny\"大众, \公众无依赖\"大众, \"大众极简\"大众, \"大众极速\"大众 乃至 \"大众Xxx走天涯\公众 等等, 以烘突\公众轻量\公众这个主题. 但是作甚\"大众轻量\"大众, 老码农原以为很清楚的观点, 反思二三, 却创造自己对此的理解竟然并不清晰. 于是决定写下此篇博文记述下自己对此的思考
\公众轻量\"大众 者, 重在于\"大众轻\公众, 无依赖且小 jar 包一定是轻了. 下一个问题是为何要 \公众轻\"大众, 或者说 \"大众轻\公众 给开拓/掩护带来何种好处. 如果说轻量定义限定于 \"大众依赖少且小 jar 包\公众 (下面且称之为苗条框架), 可以急速得到的好处大致有:
易于上手易于调试降落乃至肃清由于依赖过多引发版本冲突的可能如果须要, 便于开拓扩展功能, 乃至直接魔改核心代码然而天下上并没有永久免费的面包, 也没有银弹, 就上面的\"大众轻量\公众深入思考下去, 也会有不一样的创造. 逐条分解一下:
易于上手 - 对新手当然再好不过, 然而新手只是一个阶段, 而且是程序员生涯中比重很小的一个阶段. 易于上手当然很主要, 但作为压倒统统的目标来设计框架就没有太大必要了. 从另一方面来讲, 易于上手也不仅仅是苗条框架的专利, 设计简洁的 API, 供应足够的文档和示例项目都可以让框架变得更加易于上手.易于调试 - 平心而论, 这一点至关主要, 由于这是每个开拓职员时候面临的问题. 设想一下, 每次进入运用代码都有超过 30 个堆栈, 途中还要历经 N 个循环, 每次都有 20 个迭代; 从运用代码返回之后也如此, 这样的日常大概会让开发职员忧郁到去世. 然而是否只有苗条框架才能做到易于调试呢? 此处大大有思考余地, 元芳你怎么看?降落乃至肃清由于依赖过多引发版本冲突的可能 - 少依赖自然也就不太可能有版本冲突的可能; 可是为了减少版本冲突的可能就不要依赖了, 怎么看都有一种因噎废食的觉得呢? 况且 Maven 体系的涌现不便是为了管理依赖版本的繁芜度吗?如果须要, 便于开拓扩展功能, 乃至直接魔改核心代码 - 很好的情由, 奈何只有一次性代价. 魔改核心代码之后要追赶原项目的新版本就须要一直地做 catch-up, 除非大家自此处罚道扬镳. 至于开拓扩展功能, 也并非少依赖小 jar 包的专利, 只要文档好, 生态大, 自然扩展项目滚滚而来, 君不见 Spring 的扩展部队比灭霸的还要厉害么由此看来纯挚以苗条论英雄也有失落之偏颇的地方. 而依赖这个观点原来衍生自\"大众重用\"大众, 完备地否定\"大众依赖\"大众也便是在一定程度上否定了\"大众重用\"大众. 老码农对此表示不能赞许.下面就 \"大众轻量\公众 这个观点连续思考
3. 我对\"大众轻量\"大众的理解老码农觉得轻量不应该是对框架本身代码量和依赖的衡量, 更为确切的讲, 用户(开拓)玩起来的觉得才是定位框架轻量与否的指示. 钢铁侠的盔甲自重一定是很大的, 玩起来却是轻量得很, 险些可以随风起舞; 当然嫦娥仙子的天衣想来也一定轻量, 和钢铁侠的盔甲比较却有各自有妙处. 由此可见, 用轻量来描述框架实在并不能确切地表达各自优点特性.
3.1 初始化项目的轻量
若是框架依赖浩瀚, 启动一个空项目须要四处寻求依赖包 (貌似 maven 之前的天下差不多都是如此), 一定觉得不会轻量. 即便有了 maven, 但是 pom.xml 文件一写便是洋洋数百行代码, 也会觉得重重的. 当 pom 文件能用 10 来行乃至 50 行以内写出来, 一页纸可不雅观全貌, 几遍依赖再多, 也不会以为很重. 按此标准来看, 传统的 SpringMVC 一定重于 SpringBoot + Starter 的项目. 当然苗条框架在这里也一定轻量, 无论是采取当今的 maven 办法, 还是拿一个 jar 包放进 lib 目录的石器时期模式都不会很重.
3.2 代码中的轻量
这里面的内容就太多了, 只能勉强挑拣几个讲述一二.
3.2.1 框架的表达力与代码量
当框架有足够的表达力的时候, 运用的代码必定可以以少克多, 且不影响阅读 (非常主要!). 举个 Spring 表明改进的例子:
// 以前的表达@RequestMapping(value = \"大众/path\公众, method = \"大众POST\"大众)public void doJob() {...}// 新的表达@PostMapping(\"大众/path\公众)public void doJob() {...}
毫无疑问新的表达减少了手腕疲倦综合症发生概率 5%, 且提高了代码可读性 5%. 由此可见框架表达力在提高生产力, 延长码农利用寿命方面有非常主要的浸染. 这是我们希望看到的轻量.
3.2.2 框架对付高下文环境的应变能力
一个 HTTP 要求附带了大量的高下文信息, 比如 Accept 头便是用来见告做事端, 客户端须要何种相应. 对付下面的代码:
@GetAction(\"大众/employees\"大众)public Iterable<Employee> list() { return employeeDao.findAll();}
如果要求的 Accept=application/json 框架能自动序列化 Iterable<Employee> 为 JSON 数组, 而当 Accept=text/csv 框架能自动天生 csv 下载文件, ... 这样的框架一定减少了开拓处理各种输出格式的包袱, 少了很多干系代码, 这也是我们希望看到的轻量
3.2.3 框架对付打算环境的适配能力
实例化一个掌握器是否该当单例, 还是每个要求都须要新的掌握器实例, 这须要回答对打算环境的哀求. 大略地说如果掌握器实例没有自己的状态, 就该当采取单例, 例如:
@UrlContext(\"大众employees\"大众)public class EmployeeService { @Inject private EmployeeDao employeeDao; @GetAction public Iterable<Employee> list() {return employeeDao.findAll();} @PostAction public Employee create(Employee employee) {return employeeDao.save(employee);} ...}
上面的掌握器 EmployeeService 只有一个字段 employeeDao, 倘若该字段是无状态的, 那 EmployeeService 也该当是无状态的, 因此框架会自动采取单例来得到掌握器实例.
下面是例子则是不同的情形:
@UrlContext(\"大众my\"大众)public class MyProfileService { @LoginUser public User me; @GetAction public User getMyProfile() {return me;}}
这个掌握器有一个状态字段 me, 该字段在每次要求进来的时候通过 token(或者 cookie) 绑定到当前登任命户, 因此每次处理新的要求必须初始化新的 MyProfileService 实例.
在上面的示例代码中我们并没有看到运用利用特殊的手段 (比如加上 @Singleton 表明等) 来关照框架该当如何初始化掌握器实例, 这是框架自动适配当前打算环境的能力; 这种能力可以让开发职员写出更加轻量的代码.
3.2.4 框架对付运用参数类型的识别和处理能力
这一点对付 Web 做事框架尤其主要, 在要求端供应的数据是没有类型的 (即便是 JSON encoded 的数据也只有有限数据类型), 而做事真个 Java 工具比然是有自己的数据类型, 因此自动将要求参数按照既定规则映射到 Java 数据可以节省运用大量的开拓韶光. 例如下面的要求处理方法:
@PostAction(\"大众employees\"大众)public Employee create(Employee employee) {return employeeDao.save(employee);}
能自动处理要求参数到 Java 参数 Employee 的绑定
基于 form 的要求 (ContentType=application/x-www-form-urlencoded)
employee[firstName]=三employee[lastName]=张employee[email]=zhang3@comp.com...
基于 JSON 的要求 (ContentType=application/json)
{ \"大众firstName\公众: \"大众三\公众, \"大众lastName\公众: \公众张\"大众, \"大众email\公众: \"大众zhang3@comp.com\"大众, ...}
能基于 Content-Type 头自动实现对要求参数到 Java 声明参数的绑定能大大减少运用的代码量, 从而带来开拓职员喜闻乐见的\"大众轻量\公众.
3.3 对开拓支持的轻量
这一点在上面 Playframework 先容的时候曾经提到过. 老码农认为和代码轻量比较, 框架对开拓支持的轻量同样主要.
3.3.1 开拓模式与产品模式
将框架运行时分为开拓模式与产品模式是 PlayFramework 最先引入 Java Web 做事框架的. 这个区分可以让框架作者惬意地引入开拓时的支持而无需担心对运行时性能或者安全的影响. 以下描述都基于开拓模式谈论
3.3.2 热加载
框架监控文件系统的变革, 并在须要时重新加载更新后的源代码或者配置文件, 让开发职员只须要在浏览器上点击 F5 重新加载页面即可不雅观察到代码变动带来的变革; 全体过程在几百毫秒到几秒之内发生. 这一点带来的生产力提高上风太大了. 老码农自己曾经在 SpringMVC 上开拓项目, 每次重启做事大概须要 10 秒旁边, 韶光虽然不是很长, 但全体开拓反馈环因此停息带来的烦懑实在是很难忍受. 当开拓框架有了热加载支持之后开拓的办法都发生了一些变革. 而带来的身心愉悦就不多说了. 开拓时热加载可以让开发感想熏染到喜好的轻量.
3.3.3 开拓时缺点提示页面
开拓过程中缺点难免, 倘若框架能供应一些方便让运用开拓迅速定位缺点点, 也能带来轻量的觉得:
当路由找不到时:
当程序编译缺点时:
当程序运行时出错时:
当模板页面出错时:
3.4 API 文档的轻量
前后端分离渐成主流的形势下, API 文档愈发主要. 相应的工具 (如 Swagger) 也应运而生. 然而 Swagger 须要运用加入额外表明, 这是让人感到轻微重滞的地方:
@ApiOperation(value = \"大众View a list of available products\"大众, response = Iterable.class)@RequestMapping(value = \"大众/list\"大众, method= RequestMethod.GET,produces = \公众application/json\"大众)public Iterable list(Model model){ Iterable productList = productService.listAllProducts(); return productList;}
倘若框架直接从 JavaDoc 中天生文档则觉得又要轻量一点, 例如:
/ Create a bookmark. Normal operation It shall add a bookmark successfully with URL and brief description provided and respond with 201 and new bookmark ID. Exceptional cases It shall respond 401 if a guest user (user that not logged in) submit request to add bookmark It shall respond 400 with error message \"大众URL expected\"大众 when a logged in user submit request to add bookmark without URL provided It shall respond 400 with error message \"大众description expected\"大众 when a logged in user submit request to add bookmark without description provided. Refer: [github issue](https://github.com/act-gallery/bookmark/issues/3) @param bookmark an new bookmark posted @return ID of the new bookmark / @PostAction @PropertySpec(\公众id\"大众) public Bookmark create(@Valid Bookmark bookmark) { AAA.requirePermission(AppPermission.PERM_CREATE_BOOKMARK); bookmark.owner = me.email; return bookmarkDao.save(bookmark); }
天生 API 文档如下:
此处框架直接将 JavaDoc 的内容格式化为 API 文档描述, 同时天生要求 JSON 示例以及返回数据示例, 运用开拓除了在方法的 JavaDoc 上写清楚描述之外并没有做任何额外事情; 而前端已经可以得到非常清晰的 API 文档. 这也是对开拓大大有益的文档的轻量
3.5 测试的轻量
Web 做事框架的测试麻烦开拓皆知. 就一个大略的 HelloWorld 程序, 其测试代码大致可能为:
@RunWith(SpringRunner.class)@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)public class HttpRequestTest { @LocalServerPort private int port; @Autowired private TestRestTemplate restTemplate; @Test public void testGreetingService() throws Exception { assertThat(this.restTemplate.getForObject(\"大众http://localhost:\"大众 + port + \"大众/\"大众, String.class)).contains(\"大众Hello World\公众); }}
这样的测试对付开拓来讲实在是有点重. 实际上完备可以采取轻量得多的办法来表达相同的意思:
Scenario(Hello Service): interactions: - request: get: / response: json: result: Hello World
运行测试当然也该当轻量:
命令行(CICD 环境)下运行:
或者在开拓时调试运行:
自动测试之以是难, 难在写测试用例的麻烦. 如果框架能够以一种大略的办法让开发写测试用例, 且支持易行的办法来运行测试用例, 这种轻量化将让自动测试不再成为开拓职员的阻抗, 而是一种动力.
3.6 支配的轻量
传统基于 Servlet 的支配并不是一个很舒适的过程. 老码农理解的支配轻量可以是:
直接打包 -> scp 上传 -> 运行远端脚本停息做事并解包重启做事. 这个过程该当可以在 Jenkins 里面大略配置完成.
或者可以轻微前卫一点, 直接打个 docker 包?
4. 总结
老码农最近对 Java web 做事端框架中的 \"大众轻量\"大众 做了一点自己的剖析与思考, 在本文等分享出来. 希望能够为各位 Java web 端玩家带来一点不同的见地, 欢迎大家在评论中就这方面揭橥自己的意见, 只要有道理, 赞许与反对都是好评论.
来源:https://my.oschina.net/greenlaw110/blog/3065695
作者:开源博客社区-罗格林;© 著作权归作者所有;如有侵权,请联系删除.