持续轻量级Java EE开发:编写可测试的代码(txt+pdf+epub+mobi电子书下载)


发布时间:2021-03-05 18:31:05

点击下载

作者:崔婧雯

出版社:电子工业出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

持续轻量级Java EE开发:编写可测试的代码

持续轻量级Java EE开发:编写可测试的代码试读:

前言

简约是复杂的最终形式。——Leonardo DaVinci

现代Web 软件开发的发展日新月异。近些年,大家看到了客户端状态向服务器端移动的趋势,现在又在往回自我纠正。虽然JavaScript 的作用明显,但是关于其价值,两个工程师可能会给出三个意见。HTML5 开创了在浏览器里支持富媒体和并发的新时代。在颇具实力的NoSQL 系统的挑战下,统治业界40 年之久的关系数据模型开始有些过时,同时版本控制存储也经历了实现和范式的改革。

可用的工具正在不断变化,要从中做出选择难免会眼花缭乱。

同时,工程师们也面临着构建多用户程序所带来的巨大挑战。大家都喜欢简洁、易于维护的代码,需要代码能够高效并且安全地运行,工程师们必须保证代码的正确性。

在Java 的世界里,很多答案来自于伴随Java 企业版(Java Enterprise Edition)发布的一系列规范。这项努力的首要目标仍然是:隐藏软件开发固有的复杂语法,并且尝试根据需求提供清晰的标准模型。换句话说,Java EE 平台包括很多不断演变的工具包,而其中一个可能不太可靠。

因此几年后,大家开始填补那些Java EE 没有规范的漏洞,最终掌握了一个可以激发想象力,并且经证明比最初设想更为灵活的测试框架。很明显,为了充实想法以便更好地分享所收获的经验教训,并不需要罗列任何特别的技术。开发人员一直想拥有一张详尽的地图,能够帮助理解Java EE,及其附属框架和服务。

本书不会详细介绍某个规范,这些资料可以在别处找到,因为本书认为通过解决方案(Solution)来开始学习并没有太大意义。

相反,本书从问题(Problem)开始。我们为企业级Java 的可测试开发选择了以用例为中心的方案,并且在一些理论探索和必要的背景介绍之后,每章都会解决一个高层次的问题。本书提出的解决方案涵盖范围从用户界面到持久化存储,并且会涉及许多标准和第三方项目。所有的示例都是可执行的,还可以在相关网站的生产环境上运行,从而加以验证。

新手能够借助本书达到企业级Java 开发人员的水平,能够从零开始将一个空白存储库建设成完整部署在公有云之上的公开应用程序。所有的程序员都能够从本书中找到极具吸引力的方法,用来测试种子数据,向客户端推送事件,与分布式数据网格交互,验证用户界面,等等。

很简单,本书的目标是将复杂的事情变得简单些。幸运的是,这会带来更高的工作效率和更多的乐趣。

至少,这是我们采用本书中提到的技术时得到的经验。

本书使用的排版约定

如下是本书中使用的排版规范:

斜体(Italic)

表示新名词、URL、电子邮件地址、文件名、文件扩展名。

等宽体(Constant width)

用于程序列表,以及在段落中引用的程序元素,比如变量、函数名称、数据库、数据类型、环境变量、语句和关键字。

等宽粗体(Constant width bold)

显示命令或其他应该由用户键入的文本。

等宽斜体(Constant width italic)

显示应该由用户提供的值或上下文确定的值所替换的文本。该图标表示小窍门或者建议。该图标表示一般注解。该图标表示警告或者注意事项。

使用代码示例

从http://continuousdev.org可以下载辅助资料(代码示例、练习等)。第4章里有详细介绍。

本书帮助读者完成自己的工作,所有的内容使用Creative Commons Attribution-ShareAlike 2.0 Generic(http://creativecommons.org/licenses/by-sa/2.0/)许可证,本书邀请社区贡献了很多工作,包括特性请求、印刷错误勘正,并且通过GitHub Issue Tracker(http://bit.ly/1e7kQRD)改进加强。读者可以在遵守许可证的前提下重用所有文本或者示例,但需要注明出处。更多细节请查看许可证。

出处通常包括标题、作者、出版社和ISBN。例如:“Andrew Lee Rubinger 和Aslak Knutsen撰写的Continuous Enterprise Development in Java(O’Reilly)。Copyright 2014 Andrew Lee Rubinger and Aslak Knutsen,978-1-449-32829-0.”

Safari®Books Online

Safari Books Online(http://my.safaribooksonline.com/?portal=oreilly)是按需的数字图书馆,以书籍和视频的形式,提供来自技术和商业领域的世界顶级作家的专业内容。

技术专家、软件开发人员、Web 设计师,以及业务和创新专家将Safari Books Online 作为其主要资源,用来研究、解决问题,学习以及认证培训。

Safari Books Online 为企业、政府机构和个人提供一系列产品组合和付费项目。订阅者能够访问完整可搜索数据库里的数千本书、培训视频和正式出版前的手稿,涵盖的出版社包括O’Reilly Media、Prentice Hall Professional、Addison-Wesley Professional、MicrosotPress、Sams、Que、Peachpit Press、Focal Press、Cisco Press、John Wiley&Sons、Syngress、Morgan Kaufmann、IBM Redbooks、Packt、Adobe Press、FT Press、Apress、Manning、New Riders、McGraw-Hill、Jones&Bartlett、Course Technology,等等。在线可获得更多有关Safari Books Online 的信息。

如何联系我们

请将对本书的评价和存在的问题通过如下地址告知出版者。

美国:

O’Reilly Media,Inc.

1005 Gravenstein Highway North

Sebastopol,CA 95472

中国:

北京市西城区西直门南大街2号成铭大厦C座807室(100035)

奥莱利技术咨询(北京)有限公司

本书有自己的网页,里面列出了勘误表、示例和所有额外信息。从http://oreil.ly/continu ous-enterprise访问该网页。

关于本书的评论或者技术问题,请发送邮件到bookquestions@oreilly.com。

要获取更多关于我们的书籍、课程、会议和新闻,请访问网站http://www.oreilly.com。

Facebook 主页:http://facebook.com/oreilly

在Twitter 上关注我们:http://twitter.com/oreillymedia

YouTube 上的视频:http://www.youtube.com/oreillymedia

鸣谢

首先,我要由衷感谢Arquillian 社区:来自世界各地的天才们贡献时间和知识,帮助改进项目,从代码到写作,再到演讲和在Internet 上的呼喊(是的,我们注意到你了)。

特别感谢所有Arquillian 的模块领袖:Karel Piwko、Bartosz Majsak、Lukáš Fryč、Dan Allen、Stefan Miklosovic、Jakub Narloch、Gerhard Poul、John Ament、Jan Papousek、Bernard Labno、Ståle Pedersen、Ken Finnigan、Tolis Emmanouilidis、Ales Justin、Martin Gencur、Vineet Reynolds、Davide D’Alto、Jean Deruelle、David Blevins、Mark Struberg、Thomas Diesler、Romain Manni-Bucau、Logan McGrath 和Alexis Hassler。

特别感谢Sarah White 和Cheyenne Weaver 给出了视觉识别和书目大纲,是你们让这本书看上去这么好!

还要感谢完成本书过程中帮助改正内容和给予评论的所有人。

谢谢Meghan Blanchette 坚持推进我们的工作。如果没有你的帮助,很可能(也许是肯定)就不会有这本书的出版。

放在最后但同样重要的是,由衷感谢我的朋友Adam Bien 为本书作序。

本书献给我们的工作诞生于此,兴起于此,并且仍在持续演进于此的社区。第1章持续性

如果每个人都一起前进,那么成功便自然而然。——Henry Ford预防的真理

有时觉得冥冥中总有一些东西在破坏我们的工作。在某种程度上,这是真实的:自然并不喜欢秩序。这样的熵体现在很多方面:开启一个新的网络套接字,路由器可能会坏掉。写入一个文件,磁盘可能已经满了。没有检查输入的有效性,程序可能会意外地“爆炸”。

导致失败的可能原因无限多,并且不可避免。作为自己所写代码的质量守护者,我们有两种战斗策略:被动的和主动的。被动的错误处理

俗称为“救火”,被动处境让我们不得不采取行动。大多数情况下,不尽人意的情况已经出现,现在需要解决:

1. 错误的起始原因,如果在我们的控制下

2. 代码的未受保护区域可能造成更大的破坏

3. 错误发生后生成的遗留物

曾经翻看过数据库的二进制日志文件,想将数据恢复到一致性状态的人都可以证明,处理违反预期的执行所导致的紧急情况会浪费很多时间。在问题出现时就处理要求一定的即时性,日常工作也可能因为解决更紧迫的问题而暂停。

显然,如果可以避免的话,最好不要选择被动模型。主动的质量策略

从1947 年起,“只有你能防止……火灾”成为美国森林服务(United States Forest Service)的标语,用以强调在灾难发生前,限制导致其发生的因素的重要性。

错误的预防就是围堵政策。在发生故障的情况下,我们需要尽快了解问题,并在它可能导致更大的灾难前解决它。考虑以下简单代码:

假设用户不小心将null 传给函数welcome(String),那么返回的字符串将是:

Hello, null

这是因为Java 语言规范第7 版(Java Language Specification Version 7)(http://bit.ly/1e7k LNX)的15.18.1 节(http://bit.ly/1e7kJW5)规定:与单个String 操作符的连接会将其他操作符的结果做字符串转换。因此,根据5.1.11 节(http://bit.ly/1e7kMBr)的规则,空指针被表示为String“null”。

这并不是期望的结果,但是的确会出现这个问题,因为缺少防御性代码。通过增加先决条件的检查,给用户抛出Exception,来阻止后续程序的执行,这样便可以实现welcome(String)函数的加强:

快速失败(fail-fast)策略在开发过程中和在运行时同样重要。知道如何限制错误的影响范围仍然是一个庞大的研究和细化课题。幸运的是,软件开发流程的研究提供了很多基于实践的模型。软件开发流程

方法论。教义。范式。无论如何称呼它,流程(或缺少一个!)是在日常工作中遵循的,指导构建软件方案的脚本。通常受质量和效率的中心主题的启发,开发工作流的模型可能是一种强大的工具,能够帮助避免你和你的团队朝着事倍功半的方向发展。该领域有许多描述详尽的方案,在为项目选择合理模型时,了解这些方案的动机能够帮你做出正确的决定。串行模型

串行或顺序流程,从项目起始到完成的过程中都遵循线性路径。在开发周期的每个阶段结束时,下一个阶段随之开始。之前的阶段通常都不再重复,这个模型被形象地表示为一系列步骤,如图1-1所示。图1-1:瀑布式模型

开发从一个阶段流向下一个阶段,形象地称为“瀑布”,通常和串行模型相关联。也被称为“详细设计方式(Big Design Up Front)”,该流程严重依赖于项目开始时就能全面理解所的有需求。支撑瀑布式开发(Waterfall Design)的主要理论是“两次评估,一次裁剪”:详尽评估所有的部分,目标是避免返工和修正,从而减少浪费的时间。本书认为,该策略适合于开发周期长并且只发布一个版本的项目。

虽然该模型可能适用于零售软件,但是串行流程永不回头的理念使其无法用于构建适应性强的代码,该模型不是为支持不断改变的需求而设计的。对于不断变化的需求,最好是采用一种可以随时访问的模型,其中开发的每个阶段都允许重新再来(或者同时可以进行多个阶段!)。迭代模型

与瀑布模型(Waterfall Model)描述的线性工作流形成鲜明对比的,是一套众所周知的迭代设计,它鼓励改变并且推崇并行。通过把一个大问题分解成更易于管理的多个组件,我们就可以单独解决每个小问题。另外,可以选择在第一遍时粗略解决,然后在后续重复周期里进一步完善解决方案,这也就是所谓的“迭代”流程。

极限编程

也称为“XP”,极限编程(Extreme Programming)是一种准则,在开发流程的每个阶段引入反馈回路。这一实践方法在20 世纪90 年代末至21 世纪初开始流行,XP 在通信和其他社会层面被赞誉为最重要的主题。图1-2展现了一个典型的工作流。图1-2:极限编程里的迭代反馈回路

Kent Beck 撰写的Extreme Programming Explained:Embrace Change,Second Edition(Addison-Wesley,2004)一书详细介绍了XP 的完整理念,主要原则归结如下:

· 短开发周期。

· 每日简短会议。

· 结对编程(Pair Programming)、团队所有权(Team Ownership)和责任感(Accountability)。

· 只做需要现在就完成的事情,推迟非重要的工作。

· 尽量早,尽量频繁地收集所有stakeholder 的反馈,不仅仅局限于程序员。

· 测试驱动开发(Test-Driven Development)。

 – 先写自动化测试,然后修改或增加主代码,直到测试通过。

实际上,XP 和其他模型都是受Manifesto for Agile Software Development 一书(http://agil emanifesto.org/)的启发,都是对其中描述的各种不同迭代策略的实现。测试即开发

将测试从车尾移到引擎。——Tim Ottinger 高级顾问

不论你的团队采用哪种开发方法,也不论多么严格地遵守其原则,最终都需要确保代码能够工作。当然,可以部署应用,并且让人按照测试计划手动完成测试,但是只要有可能,将测试的执行自动化会更为高效,并且更少出故障。所以我们需要编写测试代码。

但是本书认为测试的作用并不仅仅在于简单地保证代码能够按预期工作。

当编写测试时,你就是所开发API 的用户。你能体会到其使用是否直观,也能发现文档的缺失。你可能会发现它太烦琐或者太丑陋,最重要的是:能够尽早重新评估API 的设计。你能够从目标用户的立场看问题。

此外,如果在开发业务逻辑的同时编写测试用例,工作会更加有趣。你能够知道某个特性什么时候完成,实时看到具体的反馈会让你更加满足。测试驱动开发的支持者甚至在功能实现前就编写测试用例。从我们的经验来看,应该在构建主要代码时编写测试用例,这样开发和测试两边的经验可以相互促进。

自动化测试可以有多种形式,本书会使用其中一部分。测试的分级

面向测试的软件开发流程的支持者用如下方面衡量测试:

验收

断言代码满足业务需求

黑盒

不考虑其内部实现,断言API 的协定工作正常

兼容性

断言代码和外部组件工作正常,例如,一个Web 应用需要在IE、Chrome、Firefox、Safari 和移动设备上都能正确显示

功能

断言代码满足从业务需求衍生出的技术需求(比如,所有的功能按照预期工作)

负载/压力/性能

断言并衡量系统在负载过轻时如何处理输入,随着流量的增加如何平滑地退化

回归

断言已经纠正了之前发现的错误,或者现有特性仍然工作正常

Smoke

完整测试套件的子集,意图快速运行,并且提供简化环境下系统完整运行情况的反馈

白盒

考虑其内部实现,比如,特定的数据结构和构造等,断言API 按协定工作。

一个测试良好的应用程序所包含的测试应该涵盖以上提及的大多数方面,我们能够根据范围进一步组织这些类型。单元测试

单元测试的目的是验证某个单一功能是否可以按预期独立运行。单元测试具有快速、简单、容易运行和细粒度的特点,在白盒测试中使用时可以深入实现细节。

例如,每个Java 对象都继承了方法Object.hashCode()和值相等性测试方法Object.equals(Object)。根据API 协定,值相等的对象的hashCode 应该不相等,如下所示:

上述测试用Java 语言的assert 关键字实现,是一个经典的单元测试例子:它检查可能的最小不变式(上例中,比较MyObject 的equals()和hashCode())。很多专家建议一个单元测试只包括一个断言。以我们的经验,这是一条很好的指南,但如上例所示,用常识来判断,如果需要多个断言来判断不变式的所有参与者都以预期形式存在,那么就使用多个必需的断言。

当某个单元测试需要来自无关组件的输入时,通常使用mock 对象。Mock 提供了测试中可使用的另一种实现,能够帮助开发人员:

· 模拟错误条件。

· 避免启动一个昂贵的进程或代码路径。

· 避免为了测试所需而依赖于第三方系统,它可能并不可靠(甚至不可用)。

· 避免依赖于某个提供非幂等(不可重复)值的机制。

 – 例如,一个随机数生成器或者依赖当前时间的东西。

尽管模拟对象在测试领域绝对占有一席之地,但是在企业级开发中,本书建议还是应该限制其使用。Java 企业版基于POJO(Plain Old Java Object,普通Java 对象)组件模型,这使得用户能够直接实例化servlet、Enterprise JavaBean(EJB),以及上下文和依赖注入(CDI),这有助于在简单调用里验证业务逻辑。然而,Java EE 的真正威力在于组件之间的松耦合,mock 并不负责容器所提供部件之间的连接。要想完整测试一个应用程序,必须测试整个运行时,而不是只简单测试自己写的代码。因此,我们需要比单元测试更加全面的解决方案来完成验证。集成测试

假定想要构建一段管道,从附近的水库将水引入到净化设施里。之前描述的单元测试负责确保每个部分的管道无泄漏并且质量良好。但是,整体大于各部分的总和:水仍然有可能从裂缝之间渗出。

软件也是如此,我们必须检查组件彼此间是否能够很好地协同工作。Java EE 更是如此,这里依赖注入非常常见。一个bean 并非显式绑定到另一个bean 上,这样的设计很好,但最终仍然需要依靠容器将它们连接起来。如果元数据或者配置不正确,注入点可能就不是所期望的。这会导致部署异常或更糟的事情发生,因此,要求测试必须覆盖组件之间的交互。

本书所谈论的集成测试,处在容器的上下文中。从历史上看,与应用程序服务器交互是出了名的难以测试。所以对许多人来说,Java EE 是一个“坑”。本书正是想要清晰描述以可测试的方式构建企业应用程序的技术。尽管许多人可能认为这里的讨论和集成测试相关,但是相反,我们认为这更多是关于开发的,而集成测试是其中重要的一部分。

从这个意义上来说,测试就是开发。基础测试框架

大家可能会认为,容器服务的确会帮助减少应用程序代码的复杂性。依赖注入将用户从手工连接中解脱出来,而声明式安全和事务管理等特性帮助用户专注于业务逻辑。不幸的是,这些并不是免费的:寻求框架或应用程序服务器帮助的代价是增加了另一个集成点,而每个集成点必须使用集成测试加以验证。

Java 内置支持java.lang.Assertion 错误和assert 关键字,在正确的上下文中使用时,它们都是很好的工具。因为只有在使用-ea 开关启动Java 运行时才会分析assert 的断言,所以如果在生产环境中禁用该功能,就无须担心这些额外检查会对性能产生影响。因此,测试内部代码时使用assert 合情合理,例如:

上述代码的可见性是private,因此无须考虑对用户的输入做先决条件检查;参数username 必须由已有代码提供。因此,在生产环境中无须测试。

当然,这里的断言可能会有所帮助,但是它们并不是测试。测试验证代码路径以及一个或多个后置条件。比如,可能会编写如下客户端代码,来验证“主动的质量策略”一节示例中的公有函数welcome(String)是否按预期正常工作:

就代码覆盖率而言还算好。我们已经确认welcome 方法能够按预期正常工作,甚至验证了函数是否能够正确地阻止null 作为输入,从而防止null 指针让之后的事情变得更加复杂。

但是当自己编写基于main(String[])的测试客户端时,这就有了很大的区别。看看所有仅仅为了让程序运行起来所需的样板文件,再看看测试代码本身!正如使用框架和组件模型来减少冗余一样,在业务逻辑里,可以利用一些流行的库来帮助精简测试。JUnit

JUnit 测试框架(JUnit Test Framework)(http://www.junit.org/)是Java 领域最广为人知的测试框架之一。最初从Kent Beck 为Smalltalk 编程语言所做的测试工作(http://en.wikiped ia.org/wiki/Kent_Beck)中孕育,现在在Maven 中央存储库(Maven Central Repository)(ht tp://search.maven.org/)里,JUnit 已经是除了运行Maven 本身的库以外,下载量最多的artifact(截至2012 年8 月)。

使用JUnit 重构WelcomeJDKTest 类,代码如下:

收获的第一个好处是,不再需要main(String[])方法,也不需要手动调用测试方法。取而代之,JUnit 将忠实地执行任何生命周期(比如@Before)或测试(用@Test 注释)的方法,并且向其运行器报告结果。其次,可以访问JUnit 库(比如org.junit.Assert 中的一些便利方法),来帮助减少编写断言所需的代码量。

JUnit 也有广泛的IDE 支持,使得开发过程中测试的执行更为容易。比如Eclipse 中可用的上下文菜单,如图1-3所示。

与我们自己编写的main(String[])测试客户端相比,JUnit 还支持报告。在IDE 中以图表的形式展现,如图1-4所示。

我们通常会使用持续集成服务器来处理build,并且提供代码基的可审计视图。在更加正式的构建过程中,可能会输出到XML 文件里,之后使用插件进行分析。这有助于持续追踪失败的测试以及测试总数。比如,可以使用如图1-5所示的Jenkins 持续集成服务器(Jenkins Continuous Integration Server)(http://jenkins-ci.org/),通过图表跟踪进度。图1-3:JUnit IDE 运行器集成图1-4:JUnit IDE 报告集成图1-5:持续集成测试报告

当然,JUnit 并不是唯一流行的测试框架。TestNG

如果将JUnit 视为Java 测试简单化的标准,那么TestNG(http://testng.org/doc/index.html)则提供了大量特性集,对开发人员来说具有更大的灵活性。虽然两个框架之间的差异超出了本书范围,但是它们的理念还是有相当多的重叠之处。用TestNG 重构测试代码如下:

一些注释的参数顺序和API 名称改变了,但是理念保持一致:用尽量少的代码,让框架帮助用户连接调用堆栈。

IDE 集成对Eclipse Juno 而言虽然并不标准,不过其安装非常简单(http://testng.org/doc/download.html),还提供了GUI 运行器,如图1-6所示。图1-6:Eclipse 里的JUnit 运行器持续开发

极限编程和敏捷方法的追随者可能对持续集成(Continuous Integration)(http://bit.ly/1e7n G9j)很熟悉,该方法倡导频繁修补上游的开发分支,以便捕获引入的错误。该方案包括:

· 一个权威的源码存储库(这和去中心化的版本控制系统并不矛盾,将在后面详述)。

· 一套全面的测试套件。

· 一个自动化的构建系统。

· 自动化部署。

这些一般规则几乎适用于任何现代编程语言,并不限定于某些工具,并且被整个开发社区广泛接受。

为什么本书使用持续开发这个标题呢?

除了敏捷社区所信奉的成功的意识形态和理论之外,本书还会探讨在Java 企业平台上以及扩展Java 企业平台时,会使用的具体工具和项目,来最优化解决企业Java 开发人员遇到的真实问题。

权威的Git 存储库上有本书和本书示例应用程序的源码http://bit.ly/1e7o0ox。本书网站为http://continuousdev.org,官方Twitter 渠道为@ContinuousDev(http://twitter.com/ContinuousDev)。可以通过authors@continuousdev.org 联系作者。

本书网上存储库的所有内容受Creative Commons Attribution-ShareAlike 2.0 Generic 许可证(http://creativecommons.org/licenses/by-sa/2.0/)保护,本书邀请了很多社区人员通过GitHub Issue Tracker(http://bit.ly/1e7kQRD)贡献力量,包括特性需求、印刷错误修正和内容增强。

本书印刷版以及示例源码在权威存储库中设置为Git 标签1.0.0,之后在master 分支上继续开发,来纠正勘误,添加补充材料,包括新章节和用例。社区欢迎您提出建议或者对新话题的要求。

本书用例附带的示例应用程序命名为“GeekSeek”,发布在http://geekseek.continuousdev.org。源码位于存储库的code/application 目录下,第4 章(http://bit.ly/1e7wJqQ)将详细介绍构建指令、测试以及本地运行方法。应用程序的构建工作由http://bit.ly/1e7wRGN和http://bit.ly/1e7wQ5H 上的CloudBees(http://www.cloudbees.com)提供支持。

我们希望您能够在这里找到自己感兴趣的内容,希望本书对您可测试企业开发相关的工作和事业有所裨益。

首先让我们一起了解本书主题的核心部分。第2章启动技术

朋友们点滴帮助支撑着我。——Paul McCartney 和John Lennon

大家普遍误以为制定标准规范的目的是去解决每一个问题。事实上,创建标准旨在用业界实践经验证明有效的方式来解决80%的问题。Java Community Process(JCP)(http://www.jcp.org/en/home/index)管理的Java 企业版(http://bit.ly/1e7xn7H)及其子系统也是如此。

JCP 的终极目标是在特定技术领域的Expert Group 里的所有参与者之间达成共识。如果企业赞助商和个人贡献者不同意,或者认为某个功能还没有足够成熟到应该被标准化,那么规范实施者就会遵守这个限定。这有助于孕育创造力,并且带来供应商的差异化。实际上,在讨论Java EE7 路线图(http://bit.ly/1e7xoIF)的时候,Expert Group 的成员David Blevins 扼要归纳了其中精要:“供应商负责创新,我们则负责有选择地标准化。”

尽管本书目的并不是完整介绍Java EE 的功能,但是我们的确想要统一其中的开发体验。帮助达成这一目标的是一系列启动技术,它们旨在改善EE 平台的难用之处,并且填充规范没有覆盖到的空白。

如下开源项目全部可以免费下载、使用和修改(如果修改的话,一定注意仔细查看其许可证细节)。Bootstrapping

在所有围绕Java EE 及其使用的文档里,如何快速开始看似简单,实际却有些混乱:

· 怎么才能构建源码并且部署?

· 如何组织代码基?

· 团队怎样才能在代码基上并行合作?

· 代码需要使用哪些库函数?怎么才能得到这些库?

这里的每个问题都有多种有效答案,选择灵活众多反而成了负担。因为本书将要讨论的是在每个人的环境中都能够正常工作的示例程序,所以不可避免地需要先决定是该关注代码还是该关注开发工具。下文所述项目,组合在一起时能够很好地工作,但肯定不是上面这些问题的唯一解决方案。

一种开始新项目的方式是在本地文件系统里先搭建好代码框架,创建出代码的结构,构建描述符,以及项目所需的其他资源。这样的流程通常是死板的,包括根据合理设计,使用一些命令来创建出新目录以及文本文件。并没有正式的规则来说明应该如何组织项目树,不过一些系统使用了约定俗成的方法,而另一些系统则通过鼓励使用者编写脚本或者设定每个构建任务,来自己完全控制项目的构建过程。

本书示例使用一种声明式构建工具,这一工具有标准的命令规范,这些规范不随项目而变化。Apache Maven

作为Java 自动化构建工具领域最重要的工具,Apache Maven(http://maven.apache.org/)自己定位为“软件项目管理及理解工具”。可以简单地将其看成是一个构建工具,它能够完成编译、测试和组装工作。

Maven 的一大重要功能是力求“约定优于配置”。在一系列最佳实践的指导下,使用者很可能需要大幅裁剪之前需要显式定义的元数据。此外,Maven 的操作(称为goals)被绑定到生命周期(http://bit.ly/1e7xH6o)中,这在所有基于Maven 的项目里都是通用的。比如,为了编译、测试和打包项目,需要使用命令$> mvn package。这样的标准化使得用户无须再为每个项目声明或者学习不同的构建命令。

Maven 引擎的核心是精细的依赖管理方案,能够从用户本地系统里的中央存储库(http://search.maven.org/)(或者额外配置的存储库)中通过名字解析出库。这个功能使得用户无须在版本控制系统里手动添加依赖关系,并且允许用户在构建流程里按需获取依赖关系。另外,还会自动获取并记录项目所用到的所有项目的必需依赖,如图2-1所示。当然,也有人批评Maven。主要的批评集中在这几点:

· Maven 插件版本并没有和Maven Core 版本绑定,难以保证不同的环境里可以产出可重复的build。

· 项目对象模型(Project Object Model,POM,例如,pom.xml)的语法,也就是描述项目组成的元数据,过于冗长。

· 默认的传递依赖在首次build 时会下载很多东西。如果不注意,项目可能就会继承比实际所需多得多的依赖。

· 与所定义的Maven 标准的偏差常常难以调和。图2-1:从外部存储库里获取的项目依赖

可以在Maven 外部使用Maven 结构的存储库。实际上,单独的依赖管理器Apache Ivy(http://ant.apache.org/ivy/)就提供这个功能,Apache Ivy 通常和基于任务的工具Apache Ant(http://ant.apache.org/)一起使用。基于Groovy 的Gradle(http://www.gradle.org/)力图提供Ant 的灵活性,以及Maven 的依赖管理能力。

即便如此,Maven 仍然是Java 开发中最受欢迎,使用最为广泛的工具,并且能够满足构建本书示例项目的需求。JBoss Forge

如果你曾经开发过基于Java EE 的项目(或者任何Java 应用程序),就知道需要花很多时间去创建项目布局,定义依赖关系,并且告知构建系统在编译以及执行里需要使用的相关的类路径。虽然相比于手动搭建项目而言,Maven 可以帮助用户减少很多压力,但是pom.xml 文件里还是有相当多的引用来定义各种需求。

JBoss Forge(http://forge.jboss.org/)提供了“Java EE 的增量项目改进”。类似命令行shell的实现方式,Forge 让用户能够改变项目文件和目录。Forge 能够处理的具体任务包括:

· 添加Java Persistence API(JPA)实体并且描述模型。

· 配置Maven 依赖关系。

· 搭建项目框架。

· 生成视图层,从域模型进行逆向工程。

· 部署到应用服务器上。

因为Forge 在模块化基于插件的架构(http://forge.jboss.org/plugins.html)上构建,可以扩展添加应用程序特定的额外任务。

总的来说,Forge 的目的是在开发的所有阶段让项目搭建更为容易,因此,本书会在示例项目的构造过程中使用它来提高效率。版本控制

在某个项目上需要和其他人共同合作,或者想要查看代码随时间的演进过程,就需要一定程度的版本控制。直到最近,共享代码基的同步访问领域里最常使用的方式还是客户端/服务器模型,其中,开发人员可以保留一个本地工作副本,并且将改动提交到中央服务器上,如图2-2所示。图2-2:客户端和中央版本控制系统交互

一些系统使用文件级别的锁来确保开发过程中没有任何冲突;另外一些系统允许文件粒度的同步访问,但是会在向上提交改动时提示开发人员去解决行级别的冲突。

在20 世纪90 年代到21 世纪初,部署最为广泛的客户端/服务器版本控制系统(VCS)是同步版本系统(Concurrent Versions Systems)(http://savannah.nongnu.org/projects/cvs),通常用其英文名字的首字母CVS 来指代它。虽然CVS 使得团队可以通过unreserved checkouts 来自由地在所有的文件上工作,它的缺点(包括无原子提交以及无法跟踪文件重命名)推动了Subversion(http://subversion.apache.org/)(SVN)在CVS 之后的蓬勃发展。比CVS 功能更丰富且更加稳定,SVN 在2000 年代中期到后期占据了垄断地位。

近年来,中央模型被分布式版本控制系统(DVCS)所取代,它与前者的不同之处在于,能够在任意数量的节点上存储完整的存储库,包括所有的历史信息。

这样的设计开创了一种“pull 模型”,在同一个项目里工作的开发人员被赋予了访问自己存储库的权限,能够(或者不能!)自由地包含其他人的改动。最初,对于熟悉中央化“push 模型”的用户而言,“pull 模型”很让人困扰,但是本书认为,因为有很多完整存储库共同代表同一个项目,这样利用设计上的优势轻松解决了一开始的混乱。

可以立即获得如下优势:

· 存储库操作(比如提交和历史搜索)更加迅速。

· 改变存储库状态时不要求网络连接。

· 每个存储库都是代码基历史的完整备份。

这是因为每个用户通常都是在某个本地存储库上工作,只在推送让其他人能看到的改变时,才有必要和远程存储库同步。

本书使用的是开源的DVCS Git。Git

开发Git 最初是为了协调Linux Kernel 的开发,Git 是一种DVCS,最近几年非常流行,这归功于其用户友好而名声大噪的托管网站GitHub(http://www.github.com)。实际上,本书的内容和示例都托管在GitHub 上(http://bit.ly/1e7o0ox),所有人都能够参与其中。

从高层看,为本书项目选择Git 是因为其能够实现以下功能:

真正的功能(主题)开发

创建分支十分快速,容易并且便宜。用户可以在功能X 上进行隔离开发,并且能够将改动放到主分支上。

和响应Git 事件的第三方系统集成

比如,能够通过推送本地改动到远程存储库,来触发build 以及生产环境的部署。

本地历史重写

通常可以自由地随意提交,给了用户很多“保存”点。但是,在将这些改动(有时候是会起破坏作用的改动)设为其他人可见时,最好将多个小改动“挤压”到单次提交里。这有助于保持版本历史的简洁,并且在bug 出现时能够更容易地定位问题。

再次说明,本书目的不是深入研究所使用的每种工具的机制。但是,我们会使用Git 命令,并且解释其使用方法。Scott Chacon 的Pro Git(http://git-scm.com/book)(Apress,2009)很好地介绍了Git,你可以在网上下载免费的电子版,也可以在网上零售商处买到印刷版。Java EE 的测试平台

Java EE 5 引入了POJO(普通Java 对象)编程模型,使得开发人员无须再为业务对象遵守任何特定的类层次。Java EE 6 引入的上下文和依赖注入(Contexts and Dependency Injection,CDI)(http://bit.ly/MAgJYs)通过提供类型安全的注入,进一步强化了简单业务对象的概念。

能够使用new 运算符轻松创建对象的优势和劣势并存:手动初始化对象做测试时,所处理的企业组件和目标运行时里的并不一致。EJB 在EJB 容器的上下文里才存在,servlet只在被servlet 容器创建时才是servlet。想绕开目标运行时环境来完成对象的创建并使用时,实际使用的都是模拟对象。

尽管有很多支持者认为模拟对象很有用,通过其命名就能看出,它想提供一种近似方式来模拟应用程序在生产环境的行为。记住验证运行在服务器上的代码完全符合预期,这是程序员的职责,包括那些不是自己编写的代码。应用服务器在生产环境里满负荷工作时,可能会出现一些不难定位的错误,最好能够在一个和真实生产环境尽可能相近的环境里完成测试。

这种意义上的真正的Java EE 测试是EE 平台并没有定义的领域,本书会考查一些工具,用来填补这处空白。Arquillian

Arquillian(http://arquillian.org)是极富创新且高度可扩展的JVM 测试平台,使得开发人员可以为Java 中间件轻松创建出自动化的集成测试、功能测试和验收测试。

在单元测试之后,Arquillian 可以处理所有容器管理、部署和框架初始化的工作,从而让开发人员能够关注测试逻辑的编写,无须配置复杂的测试装置,Arquillian 通过如下方式将目标运行时抽象了出来:

· 管理某个容器(或者多个容器)的生命周期。

· 将测试用例、依赖类以及资源打包到ShrinkWrap 归档(或多个归档)里。

· 将归档(或多个归档)部署到某个容器(或多个容器)里。

· 通过提供依赖注入及其他声明式服务来充实测试用例。

· 在容器内(或者针对容器)执行测试。

· 获取结果,并将其以报告的形式返回给测试运行者。

· 为了避免给开发人员的build 环境带来不必要的复杂度,Arquillian 可以和其他常用的测试框架(比如JUnit 4、TestNG 5)无缝集成,使用已有的IDE、Ant 和Maven 测试插件就可以直接启动测试,无须任何额外插件。

Arquillian 项目遵循三大核心原则:

对于任何支持的容器而言,测试必须是便携的。

将容器特定的API 放在测试之外,这使得开发人员能够通过在多种容器里运行测试来验证应用程序的便携性。这也意味着在开发过程中,可以使用轻量级容器替代完整容器。

通过IDE 和构建工具都能够执行测试。

通过使用IDE,开发人员能够跳过构建过程更快做出改动,并且在类似的环境下进行调试。这样的优势不需要牺牲通过构建工具在持续集成中运行测试的能力。

平台必须扩展或者集成已有测试框架。

可扩展架构鼓励重用已有软件,并且促进形成统一的Java 测试生态系统。不管它变得多么复杂,执行Arquillian 测试和在IDE 里选择Run As→Test,或者在构建工具里执行“测试”任务一样简单,如图2-3所示。图2-3:DCVS 存储库及其关系ShrinkWrap

从一开始,ShrinkWrap 就是为了简化Java 企业部署的测试而生的。传统的flat-file 归档使用的是ZIP 标准,迫切需要一些构建步骤来打包应用程序的所有资源。构建步骤也需要时间来完成:

但是开发人员生活在编程环境里,从编程思维切换出来运行build 是一种浪费。因此自然会这么问:“如果能够在Java 里声明一个对象来表示这个归档呢?”这就引出了一个Java API 来指代“jar”工具,一个使用直观语法的虚拟文件系统:

这个API 使得用户能够利用IDE 的增量编译特性,允许跳过构建过程,如图2-4所示。图2-4:Eclipse IDE 的增量编译

这一功能帮助达成了Arquillian 的设计目标,能够直接从IDE 里基于完整部署来运行测试。

尽管ShrinkWrap 是一种独立的虚拟文件系统,但是本书示例中会主要使用它作为Arquillian 的部署机制。我们先花点时间来看看它的使用方法。

第一步是了解ShrinkWrap 的代码。其核心部分包括三块,如表2-1所示。表2-1:ShrinkWrap 模块以及API,SPI 和实现的分离

在编译类路径上,只有API 可用,而SPI 和实现模块都是运行时所必需的。这是为了更好地强制隔离直接使用和项目内部使用的类。

在Maven 里,这些可以通过使用ShrinkWrap 依赖链(Dependency Chain)POM 在合适的范围里轻松引入,在Maven Central 里可用:

对于没有使用Maven 存储库系统的项目,ShrinkWrap 发行版让所有的模块都可以下载,并且可以根据需要手动设置依赖关系。如下是先决条件:

· JRE5+运行时。

· 没有额外的依赖。

ShrinkWrap 能够在任何Java5 或更新的运行时上运行,但是编译要求至少是JDK6。

ShrinkWrap 库的主要入口点是类org.jboss.shrinkwrap.api.ShrinkWrap。从这里可以调用create 方法来创建一个新的Archive,一个虚拟文件系统的通用视图,允许名为Asset 的内容添加到ArchivePath 的路径下。表2-2用更为通用的词语来解释ShrinkWrap的术语。表2-2:ShrinkWrap 归档类型

要创建一个Archive,只要简单选择所需的归档类型,并且可选择地提供一个名字给静态的ShrinkWrap:create 方法即可:

可以了!这就得到了第一个ShrinkWrap 归档。

当然,代表空白归档的对象没有任何意义。因此让我们看看怎么向其中添加一些内容。如之前所介绍的,内容用Asset 类来建模,因此首先看看ShrinkWrap 提供的Asset 的实现(见表2-3所列)。表2-3:ShrinkWrap asset 类型

另外,因为Asset 是一个接口,使用者可以自己实现,以提供任何基于byte,并且能够用InputStream 表示的内容。比如,如下代码展示如何将Activation 框架(Activation Framework)的DataSource 表示为Asset:

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载