当我们旨在使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
执行任何getAttribute
和setAttribute
操作吗?同样,您也可以使用@RequestScoped
来删除它们。
当然,还有@ApplicationScoped
,可以消除您可能在ServletContext
上执行的getAttribute
和setAttribute
调用。
更酷的是,像这样跟踪的任何对象都可以实现@PostConstruct
,当bean被创建时它会被调用,以及一个@PreDestroy
方法,用于在完成该“范围”(会话结束,请求结束,应用程序关闭)时通知该方法。
CDI可以做更多的事情,但这已足以让任何人想要重新编写旧的Web应用程序。
一些挑剔的事情
在Java EE 6中添加了一些东西,它们在Tomcat的舞台上没有添加。它们不需要大量的解释,但占据了“填补差距”的很大一部分。
- 支持
@DataSourceDefinition
- 支持全局JNDI(
java:global
、java:app
、java: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
这是一个相当有用的堆栈。