大多数服务器端Java应用程序(例如,面向Web或面向服务的)都希望在容器中运行。 打包这些应用程序以进行分发的传统方法是将它们捆绑为WAR文件。 这无非是具有标准目录布局的ZIP归档文件,其中包含运行时所需的所有库和应用程序级依赖项。 这种格式几乎可以互操作,并且可以部署到您喜欢的任何服务器容器,Tomcat或Jetty,JBoss或GlassFish等。
但是,还有另一种流派完全颠覆了这种模式。 通过这种方法,Java应用程序像任何普通应用程序一样被打包用于命令行执行。 嵌入式容器不是部署到容器,而是部署在应用程序本身中!
这对于使用多种语言的课程来说是一样的。 适用于Python的Django框架包括用于开发和测试的捆绑服务器,而Ruby on Rails附带了一个用于生产的嵌入式服务器。 这个概念在Java中已经存在了一段时间, Jetty专攻嵌入式利基市场。 但是,这与规范相去甚远,事实上的标准仍然是可以部署到Tomcat的WAR文件。
但是,嗡嗡声越来越大。 在去年的DevNexus会议上,我参加了James Ward的会议,当时他是Heroku的“开发者”。 捆绑您自己的容器是将应用程序部署到Heroku的基于云的平台的推荐方法,而James是一个大力支持者 。
他的会议专门针对Java和Scala的Play框架,该框架以类似于Rails服务器的方式嵌入Netty 。 与Grails不同, Grails使用Django风格的服务器进行开发,然后将其作为WAR文件发布,而Play则打算在生产过程中一直使用自己的服务器。 James在所有Java应用程序中都倡导了这种方法。
一场嵌入式冒险
我至少喝了一口Kool-Aid。 当我开始编写示例Hibernate Search by Example时 ,我想将重点放在Hibernate Search上,而不是其他任何框架或服务器问题上。 因此,我避开了Spring,并使用普通的Servlet 3.0方法编写了本书的示例应用程序。
我通常在自己的开发环境中使用Eclipse,并将其指向本地Tomcat实例以测试Web应用程序。 但是,我想为喜欢使用IntelliJ,Netbeans或根本不使用IDE的读者提供支持。 因此,我决定将构建脚本嵌入测试服务器,以便读者可以运行示例而无需安装或配置任何东西。
在Maven中使用嵌入式服务器
我的第一个目标只是从我的Maven构建脚本中启动服务器,因此读者不必安装服务器或将其集成到IDE中。 我以前看过这件事,这很简单,只需将Jetty Maven插件添加到项目的POM中即可。 读者应该能够使用以下命令构建示例应用程序并一步启动它:
mvn clean jetty:run
嗯,不是那么快。 您应该能够对静态内容进行更改,并看到更改在服务器运行时立即生效。 但是,我遇到了有关文件被锁定的错误。 在花了一些时间进行研究之后,我发现Jetty的默认设置在Windows文件锁定中效果不佳。 可以通过在一个配置文件中切换一个属性来解决此问题。
但是,您必须打开Jetty JAR文件才能获取此配置文件的正确副本。 首先,你要挖围绕本地Maven回购,并找出哪些 JAR文件破解打开(它原来是码头,web应用,而不是码头服务器)。 一旦获得了webdefault.xml文件的副本并切换了useFileMappedBuffer设置,就必须将副本保存在项目中的某个位置,并更新Maven POM以在此处查找而不是在Jetty JAR内部查找:
<plugin><groupId>org.mortbay.jetty</groupId><artifactId>jetty-maven-plugin</artifactId><version>8.1.7.v20120910</version><configuration><webAppConfig><defaultsDescriptor>${basedir}/src/main/webapp/WEB-INF/webdefault.xml</defaultsDescriptor></webAppConfig></configuration>
</plugin>
好吧……比我预期的麻烦得多,但是我可以解决这个问题。
将嵌入式服务器与其他构建系统一起使用
我知道许多Java开发人员都讨厌Maven。 因此,我想提供使用Ant构建的本书示例应用程序的版本,以说明如何适应默认概念。 那么,我应该在build.xml中添加哪一行以使Ant使用Jetty?
嗯,不是那么快。 有 用于码头Ant集成 ,但它比Maven的更繁琐。 即使您使用的是Ivy之类的依赖项管理系统,您的Ant脚本也无法为您下载和管理嵌入式服务器。 相反,您必须下载完整的独立Jetty服务器,然后手动将其片段复制到您的项目中。 谁不希望将6兆的可执行二进制文件提交到源代码管理中?
在Jetty服务器JAR上复制后,您需要手动添加另一个JAR文件以进行Ant集成。 令我惊讶的是,我发现最新的受支持版本是Jetty 7,实现了将近8年的Servlet 2.5规范。
我看到他们终于在上个月添加了Jetty 8,但是当我去年秋天写这本书时,这对我没有帮助。 我不得不为Servlet 2.5(而不是3.0)重写这个示例应用程序版本,并且开始怀疑这是否真的值得。
通过代码使用嵌入式服务器
本书的最后这一章讨论了在集群服务器环境中运行的Hibernate Search应用程序。 Maven插件纯粹是单实例的,因此我决定编写一个小的引导程序类,该类将在语法上在不同端口上启动两个Jetty实例。 通过将此类构造为JUnit测试,我仍然可以让Maven像这样自动启动它:
mvn clean compile war:exploded test
嗯,不是那么快。 启动时未注册我的应用程序的Servlet,侦听器和RESTful服务。 经过大量浪费的研究时间后,我发现有很多不同的Jetty “风味”可用,默认情况下启用或禁用Servlet 3.0功能(例如注释)。
老实说,我仍然不完全了解如何分辨“高潮”和“非高潮”之间的区别。 我只能告诉您的是,为了使注释能够正常工作,我必须将这段代码添加到我的bootstrap类中:
...
masterContext.setConfigurations(
new Configuration[] {
new WebInfConfiguration(),
new WebXmlConfiguration(),
new MetaInfConfiguration(),
new FragmentConfiguration(),
new EnvConfiguration(),
new PlusConfiguration(),
new AnnotationConfiguration(),
new JettyWebXmlConfiguration(),
new TagLibConfiguration()
}
);
...
比将WAR文件拖放到Tomcat的/ webapps文件夹中更简单,更直观,对吗?
从控制台和云使用嵌入式服务器
完成本书后,我希望将示例代码的演示版本推送到GitHub并部署到Heroku 。 从理论上讲,Heroku可以运行可以从命令行在本地运行的任何应用程序。 如果Heroku找到Maven POM,它将运行mvn clean程序包,然后执行您在名为Procfile的脚本中放置的所有启动命令。
我的程序化Jetty启动器在Maven运行的情况下运行良好。 但是,Maven在测试时正在管理我的类路径依赖项,现在我需要在没有该帮助的情况下可以使用Jetty。 Heroku在其演示Java应用程序中使用的推荐方法是将您的应用程序与Tomcat的一个文件版本捆绑在一起。 太棒了,无论如何,我还是更熟悉Tomcat!
嗯,不是那么快。 如果您的应用程序希望将数据库连接(或其他任何内容)注册为JNDI资源,那么您就自己决定了。 Heroku捆绑的Tomcat运行程序不支持JNDI设置。 嗯......也许这就是为什么Heroku的香草servlet的演示并没有真正做什么,以及为什么只演示应用程序, 并做一些事情,而不是是基于Spring。 现在,我考虑了一下,James Ward去年离开了Heroku,为TypeSafe工作,自从他离开后,Heroku就没有对其Java网站进行任何更新。 喝了
不用担心,因为有一个类似的单文件Jetty Runner ,它确实允许您将JNDI设置作为命令行参数传递。 此外,我们已经投入了大量时间来解决嵌入式Jetty的所有问题!
嗯,还是太快了。 如果您在JSP视图中使用JSTL taglib(即您生活在21世纪),那么Jetty Runner会让您陷入混乱 。 从命令行运行它时,您需要将参数传递给Java,以实现以下目的:
- Jetty Runner JAR文件
- Web应用程序的WAR文件(*)
- 在Maven构建期间生成的WAR文件的分解版本
(*)您没看错。 经过所有这些嵌入式噩梦之后,Heroku 实际上仍在使用WAR文件 !
我的Heroku个人资料最终看起来像这样:
web: java $JAVA_OPTS -jar target/dependency/jetty-runner-8.1.7.v20120910.jar --lib target/hibernate-search-demo-0.0.1-SNAPSHOT/WEB-INF/lib --port $PORT --jdbc org.apache.commons.dbcp.BasicDataSource "url=jdbc:h2:mem:vaporware;DB_CLOSE_DELAY=-1" "jdbc/vaporwareDB" target/*.war
这里有多个类加载器在工作,这使Jetty Runner可以从其类路径而不是Web应用程序的类路径加载JSTL / taglib东西。
结论
从一开始就将嵌入式服务器概念嵌入框架中,嵌入式服务器概念就没有本质上的错误。 编写Play应用程序是一种乐趣,将它们部署在Heroku上几乎是微不足道的。 在日常工作中,我使用一个名为hybris的基于Spring的商务软件包,该软件包的扩展构建系统将Tomcat服务器捆绑到您的应用程序中。 只要您不需要过多地定制构建脚本,就可以正常工作。
另一方面,该概念对于广泛的通用用途而言太脆弱和脆弱。 用管道将嵌入式服务器连接到普通Java应用程序上纯属痛苦。 您也许可以坚持别人的工作示例的安全性,但是当您的应用执行任何不同的操作时,您就可以自行解决损坏问题。 将我的嵌入式冒险带入上面,并将其与使用Tomcat的“麻烦”进行对比:
- 下载Tomcat并将其解压缩到某个位置
- 将您的WAR文件拖放到Tomcat的/ webapps子目录中
- 启动Tomcat
我获得的唯一真正优势是能够在Heroku上运行演示的能力。 但是,来自云提供商的Java支持每天都在提高。 Jelastic允许您立即将普通的WAR文件部署到Tomcat 7或GlassFish 3。 AppFog支持部署到Tomcat 6 ,即将支持Tomcat 7 。 我怀疑在不久的将来, 修改应用程序以进行云部署的想法将被视为不合时宜。
简而言之,这取决于您使用的框架。 如果嵌入式服务器是嵌入式的,那么它们可能会很酷。 如果将它们用胶带固定,则可能会很可怕。 如果我今天写的是Hibernate Search by Example ,示例应用程序构建脚本将产生两件事:一个WAR文件和一个Tomcat下载链接。
翻译自: https://www.javacodegeeks.com/2014/03/war-files-vs-java-apps-with-embedded-servers.html