除了EAR和EJB,与Tomcat这样的Servlet容器相比,在Java EE应用服务器中我可以得到什么?

23
我们使用Tomcat来托管基于WAR的应用程序。我们是符合Servlet容器的J2EE应用程序,但org.apache.catalina.authenticator.SingleSignOn除外。
我们被要求迁移到商业Java EE应用服务器。
改变的第一个缺点是成本。无论应用服务器的收费是多少,Tomcat都是免费的。
第二个缺点是复杂性。我们既不使用EJB功能也不使用EAR功能(当然不可能),也没有错过它们。
那么我看不到的好处是什么?
我没提到的缺点是什么?
提到的是...
1. JTA - Java事务API - 我们通过数据库存储过程控制事务。 2. JPA - Java持久化API - 我们使用JDBC和存储过程进行持久化。 3. JMS - Java消息服务 - 我们使用XML通过HTTP进行消息传递。
这很好,请再说一些!

好问题。我有时也在想“适当的”应用服务器与Tomcat之间的优点。 - Jonik
5个回答

59
当我们旨在使Apache Tomcat成为Java EE 6认证的Apache TomEE时,以下是我们需要填补的一些空白,以便最终通过Java EE 6 TCK。
这不是一个完整的列表,但是其中一些亮点可能即使有现有答案也不明显。

没有TransactionManager

任何经过认证的服务器都绝对需要事务管理。在任何Web组件(servlet、filter、listener、JSF托管bean)中,您都应该能够像这样注入UserTransaction
  • @Resource UserTransaction transaction;
您应该能够使用javax.transaction.UserTransaction创建事务。在该事务作用域内触及的所有资源都应该在该事务中注册。这包括但不限于以下对象:
  • javax.sql.DataSource
  • javax.persistence.EntityManager
  • javax.jms.ConnectionFactory
  • javax.jms.QueueConnectionFactory
  • javax.jms.TopicConnectionFactory
  • javax.ejb.TimerService
例如,如果在一个servlet中启动事务,则:
  • 更新数据库
  • 向主题或队列发送JMS消息
  • 创建一个计时器,在以后的某个时间点进行工作
..然后这些事情中的任何一个失败,或者您选择在UserTransaction上调用rollback(),那么所有这些事情都会被撤消。

没有连接池

要非常清楚,有两种类型的连接池:
  • 事务感知连接池
  • 非事务感知连接池
Java EE规范并不严格要求连接池,但是如果您有连接池,它应该是事务感知的,否则您将失去事务管理。
这基本上意味着:
  • 在同一事务中的每个人应该有来自池的相同连接
  • 直到事务完成(提交或回滚),连接才不应返回到池中,而不管是否有人调用了close()或任何其他方法。

在Tomcat中常用的连接池库是commons-dbcp。我们也希望在TomEE中使用它,但它不支持事务感知连接池,因此我们实际上将该功能添加到了commons-dbcp中(万岁,Apache),并且已经作为commons-dbc版本1.4的一部分存在。

请注意,仅将commons-dbcp添加到Tomcat还不足以获得事务连接池。您仍然需要事务管理器,您仍然需要容器通过Synchronization对象向TransactionManager注册连接。

在Java EE 7中,有关添加一种标准方式以加密DB密码并将其与应用程序一起打包在安全文件或外部存储中的讨论。这将是Tomcat不支持的另一个功能。

没有安全集成

Web服务安全性,JAX-RS SecurityContext,EJB安全性,JAAS登录和JAAC都是默认情况下未在Tomcat中“连接”的安全概念,即使您单独添加了像CXF,OpenEJB等库。

These APIs are all supposed to work together on a Java EE server. We had to do quite a bit of work to make them cooperate and integrate well with Tomcat's Realm API, so that people can use all existing Tomcat Realm implementations for their "Java EE" security. It's still Tomcat security, but well-integrated.
JPA Integration
Yes, you can add a JPA provider into a .war file and use it without Tomcat's help. However, you won't have access to: - EntityManagerFactory injection/lookup with @PersistenceUnit - EntityManager injection/lookup with @PersistenceContext - An EntityManager connected to a transactional aware connection pool - JTA-managed EntityManagers support - Extended persistence contexts
JTA-Managed EntityManager 的基本含义是,在同一事务中希望使用EntityManager的两个对象将都看到相同的EntityManager,因此无需显式传递EntityManager。所有这些“传递”都由容器为您完成。
这是如何实现的?简单来说,您从容器中获得的EntityManager是一个伪装者。它是一个包装器。当您使用它时,它会在当前事务中查找真正的EntityManager并将调用委托给该EntityManager。这就是神秘的EntityManager.getDelegate()方法的原因,因此用户可以获取real EntityManager并利用任何非标准API。当然要非常小心,永远不要保留对委托EntityManager的引用,否则您将面临严重的内存泄漏。委托EntityManager通常会在事务完成时被刷新、关闭、清理和丢弃。如果您仍然持有引用,则会阻止该EntityManager及其可能持有的所有数据的垃圾回收。
  • 从容器中获取的EntityManager引用始终是安全的
  • 持有EntityManager.getDelegate()的引用是不安全的
  • 非常小心地持有通过EntityManagerFactory自己创建的EntityManager的引用--您需要100%负责其管理。

CDI集成

我不想过分简化CDI,但我发现它有点太大了,许多人并没有认真看过--它在许多人的"某天"列表上 :) 所以这里只介绍一些我认为"Web开发者"想要了解的亮点。

你知道在典型的Web应用程序中你所做的所有放入和获取操作吗?整天从HttpSession中拉出东西?使用String作为键,并不断地将从HttpSession获取的对象进行强制转换。您可能已经编写了实用程序代码来为您执行此操作。

CDI也有这个实用程序代码,它被称为@SessionScoped。任何使用@SessionScoped注释的对象都会被放置和跟踪在HttpSession中。您只需通过@Inject FooObject请求将对象注入到Servlet中,CDI容器将以我描述过的事务跟踪EntityManager的方式跟踪“真正”的FooObject实例。现在你可以删除一堆代码了 :)

HttpServletRequest执行任何getAttributesetAttribute操作吗?同样,您也可以使用@RequestScoped来删除它们。

当然,还有@ApplicationScoped,可以消除您可能在ServletContext上执行的getAttributesetAttribute调用。

更酷的是,像这样跟踪的任何对象都可以实现@PostConstruct,当bean被创建时它会被调用,以及一个@PreDestroy方法,用于在完成该“范围”(会话结束,请求结束,应用程序关闭)时通知该方法。

CDI可以做更多的事情,但这已足以让任何人想要重新编写旧的Web应用程序。

一些挑剔的事情

在Java EE 6中添加了一些东西,它们在Tomcat的舞台上没有添加。它们不需要大量的解释,但占据了“填补差距”的很大一部分。

  • 支持@DataSourceDefinition
  • 支持全局JNDI(java:globaljava:appjava:module
  • 通过@Resource MyEnum myEnum进行枚举注入和
  • 通过@Resource Class myPluggableClass进行类注入和
  • 支持@Resource(lookup="foo")

次要的问题,但在应用程序中以可移植的方式定义DataSource、在Web应用程序之间共享JNDI条目以及具有“查找并注入此项”的简单功能可能非常有用。

结论

作为提醒,并非完整列表。没有提到EJB、JMS、JAX-RS、JAX-WS、JSF、Bean Validation等其他有用的东西。但至少可以了解人们谈论Tomcat时经常忽视的一些事情。
还要注意,你可能认为的“Java EE”可能与实际定义不符。通过Web Profile,Java EE已经缩小了。这是有意为之,以应对“Java EE太重了,我不需要那么多”的问题。
如果将EJB从Web Profile中剔除,您将剩下以下内容:
  • Java Servlets
  • Java ServerPages (JSP)
  • Java ServerFaces (JSF)
  • Java Transaction API (JTA)
  • Java Persistence API (JPA)
  • Java Contexts and Dependency Injection (CDI)
  • Bean Validation
这是一个相当有用的堆栈。

这部分内容"事务完成前不应将连接返回到池中"很有意思。这似乎是最明智的做法,但为什么Hibernate有hibernate.connection.release_mode,在JTA环境下默认值为after_statement,而OpenJPA有openjpa.ConnectionRetainMode,在需求时默认值为on-demand?参考文献: http://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html_single/#configuration-jdbc-properties http://openjpa.apache.org/builds/2.2.1/apache-openjpa/docs/ref_guide_dbsetup_retain.html - marcus
@marcus 注意,这些页面上并没有提到 "JTA",而是提到了事务。实际上,这些设置只适用于 <persistence-unit transaction-type="RESOURCE_LOCAL">,并且仅当应用程序通过 javax.persistence.Persistence 工厂创建了 EntityManagerFactory 时才起作用。容器创建的 EntityManagerFactory 实例将通过 javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory(PersistenceUnitInfo, Map) 方法创建。容器将直接通过 PersistenceUnitInfo 接口提供其自己管理的 DataSources。 - David Blevins
第一个明确表示:“对于应用程序服务器JTA数据源,请使用after_statement在每个JDBC调用后积极释放连接。” 我有什么遗漏吗? - marcus
我怀疑Hibernate文档建议在使用应用程序服务器JTA数据源时在每次调用后释放连接,因为他们知道只要事务处于活动状态,他们将始终获得相同的连接以供所有未来调用。这是一种不必管理已经管理好的方法。 - David Blevins

8
除非您需要 EJB 真正的功能,否则您不需要完整的 J2EE 服务器(商业或非商业)。即使没有完整的 J2EE 服务器,您也可以拥有大多数 J2EE 功能(例如 JTA、JPA、JMS、JSF)。完整的 j2ee 的唯一好处是容器将以声明性方式代表您管理所有这些内容。随着 EJB3 的出现,如果您需要容器管理的服务,则使用一个是一个好选择。
您还可以使用免费的全栈服务器,例如 Glasfish、Geronimo 或 JBoss。
例如,您可以在 Tomcat 中嵌入 Glasfish,运行内置 j2ee 容器管理服务。
如果您想要优雅地管理会话 Bean、消息 Bean、定时器 Bean,甚至进行集群和故障转移,则可能需要 EJB 容器。
我建议管理层根据功能需求考虑升级。其中一些 EJB 容器可能会使用嵌入式 Tomcat 作为其 Web 服务器,那么有什么区别呢!
有些经理就喜欢买东西。请让他们考虑捐赠城市收容所或选择 BEA。

JBoss使用Tomcat作为嵌入式Servlet容器。 - Dónal
3
读者注意,自本回答发布以来,已有几项变化,可以说这些变化正是因为像本回答一样的答案:) 现在您可以通过Web Profile获得不完整的“full stack”。现在有一个经过认证的Tomcat版本(TomEE),使其与GlassFish或JBoss处于同一水平。 CDI已被添加,并在过去一两年中证明是Java EE的主要吸引力。 - David Blevins
@David Blevins 我有一个使用大部分东西但不是很庞大的Glassfish JEE全栈应用程序。我正在考虑将其放在TomEE上。我的一个担忧是,我已经定制了JDBCRealm类来处理我的用户组关系。这对TomEE来说是一个重大问题吗(即是否更难做到)?另外,设置JMS和池/JNDI(设置JNDI数据源可以)的管理控制台怎么样?我看到了很多关于TomEE的好东西,真的想尝试一下,但并不是要听起来很聪明,但没有太多时间去搞定它。谢谢。 - Bill Rosmus
@BillR 从安全角度来看,一切都是Tomcat。Tomcat确实有一个JDBCRealm,我怀疑它与GlassFish的类似。至于其余的配置,TomEE会查看您的应用程序需要什么,并为您创建所有内容。如果您使用标准的java:comp/env名称,JNDI名称也很容易。我告诉你吧,如果你愿意写一篇关于在TomEE上尝试你的应用程序的博客文章,我将很乐意提供直接帮助(Skype、即时消息等)。这可能只需要一个下午的时间。 - David Blevins
@DavidBlevins 我很乐意参加...但需要先检查一下JDBCRealm的事情。我总是因为我的技术写作而受到赞扬。所以为了帮助,这将是值得的。接下来的几天我会比较忙,但如果你有时间的话,这个星期肯定可以。 - Bill Rosmus
@BillR 很好。这周在JavaOne大会上。下周有空。dblevins (at) apache.org - David Blevins

3
如果有人要求你转移到商业J2EE服务器,原因可能与J2EE堆栈无关,而是与非技术考虑有关。
使用商业J2EE提供的一件事情是技术支持,而这是Tomcat所没有的。
这可能对你来说不是一个考虑因素,这取决于你的Web应用程序应该满足的服务级别。 你的应用程序可以在你尝试解决Tomcat问题时停机吗?还是这将是一个重大问题?

2
成本不一定是劣势,因为有一些免费的J2EE服务器,例如JBoss和Glassfish。
你的问题假设(J2EE = Servlet + EJB + EAR),因此,如果你不使用EJB或EAR,使用Servlet容器之外的任何东西都没有意义。这显然不是事实,J2EE包括更多的内容。例如:
- JTA-Java事务API - JPA-Java持久性API - JMS-Java消息规范 - JSF-用于从组件构建用户界面的技术
谢谢, 唐纳尔

我们在没有J2EE容器的情况下使用JSF。 - dacracot
我没有提到具体的J2EE应用服务器,以免成为攻击的对象,但它确实需要付费。 - dacracot
我并不是说你需要使用J2EE应用服务器才能使用这些技术,只是想表达这样一个观点:J2EE不仅仅包括EJB、Servlets和EAR。 - Dónal

2
事实上,随着大量的软件包和库的出现,现代Servlet容器(如Tomcat)可以添加EJB容器提供的几乎所有功能。因此,如果你需要这些功能中的任何一个,你可以像点菜一样获得它们,唯一的成本是将该功能集成到你的应用程序中。
如果你现在不需要这些功能,从实际角度来看,你可能不需要它们。
尽管如此,现代EJB容器确实非常好用,并且预先集成了所有这些服务,使得使用它们变得更加容易。有时候,拥有附近和方便的功能就足以让人们探索它在他们的应用程序中的潜力,而不是将功能集成过程视为采用的障碍。
考虑到免费的EJB容器的质量,很难想象购买一个EJB容器有什么用处,特别是在你目前没有真正需求的情况下。
然而,我鼓励你实际获取一个并试玩一下,探索这个平台。Glassfish非常易于入门和使用,并且应该可以轻松地接受你的WAR文件(或者只需要进行非常小的调整)。
当涉及到运行Tomcat与EJB容器时,规则是为什么不使用EJB容器?特别是对于Glassfish而言,我发现它比Tomcat更易于使用,它的主要区别在于它可能会占用稍微更大的内存空间(特别是对于小型应用程序),但是对于大型应用程序,你甚至都不会注意到这一点。对于我来说,内存问题并不是很大的问题,但对于其他人可能是个问题。
此外,它为我提供了一个所有这些很好的功能的单一来源,而不必在网络上寻找第三方选项。

部署EAR的一个明显优势是所有的连接可以在内部指定,而不必为提供多个附加服务显式设置Tomcat。自动化的越多越好。 - Thorbjørn Ravn Andersen
非常好的答案,迄今为止这里最清晰的答案。你所说的大部分与我对此事的理解相符:如果使用Tomcat,则几乎可以添加任何JEE功能,因此没有真正需要切换,但如果从头开始,最好选择像JBoss或Glassfish这样的好的免费应用服务器可能更容易一些。 - Jonik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接