当我们以 Java EE 6 将 Apache Tomcat 认证为Apache TomEE为目标时,这里有一些我们必须填补的空白才能最终通过 Java EE 6 TCK。
不是一个完整的列表,而是一些即使有现有答案也可能不明显的亮点。
没有事务管理器
任何经过认证的服务器都绝对需要事务管理。在任何 Web 组件(servlet、过滤器、侦听器、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 消息
- 创建一个计时器以在稍后的某个时间进行工作
.. 然后其中一件事情失败了,或者您只是选择调用rollback()
,UserTransaction
那么所有这些事情都被撤消了。
无连接池
很清楚有两种连接池:
Java EE 规范并不严格要求连接池,但是如果你有连接池,它应该是事务感知的,否则你将失去事务管理。
这基本上意味着:
- 同一事务中的每个人都应具有来自池的相同连接
- 在事务完成(提交或回滚)之前,不应将连接返回到池中,无论是否有人调用
close()
了DataSource
.
Tomcat 中用于连接池的通用库是 commons-dbcp。我们也想在 TomEE 中使用它,但是它不支持事务感知连接池,因此我们实际上将该功能添加到 commons-dbcp(是的,Apache)中,并且从 commons-dbc 版本 1.4 开始。
请注意,将 commons-dbcp 添加到 Tomcat 仍然不足以获得事务连接池。您仍然需要事务管理器,并且您仍然需要容器来完成注册与TransactionManager
viaSynchronization
对象的连接的管道。
在 Java EE 7 中,有人谈论添加一种标准方法来加密 DB 密码并将它们与应用程序一起打包到安全文件或外部存储中。这将是 Tomcat 不支持的另一项功能。
没有安全集成
WebServices 安全性、JAX-RS SecurityContext、EJB 安全性、JAAS 登录和 JAAC 都是默认情况下不会在 Tomcat 中“挂钩”的安全概念,即使您单独添加 CXF、OpenEJB 等库也是如此。
当然,这些 API 都假设在 Java EE 服务器中协同工作。我们必须做很多工作才能让所有这些协作并在 Tomcat Realm
API 之上完成,以便人们可以使用所有现有的 TomcatRealm
实现来驱动他们的“Java EE”安全性。它实际上仍然是 Tomcat 安全性,它只是很好地集成。
JPA 集成
是的,您可以将 JPA 提供程序放入 .war 文件中,并在没有 Tomcat 帮助的情况下使用它。使用这种方法,您不会得到:
@PersistenceUnit EntityManagerFactory
注入/查找
@PersistenceContext EntityManager
注入/查找
- 连接
EntityManager
到事务感知连接池
- JTA 托管
EntityManager
支持
- 扩展的持久性上下文
JTA-ManagedEntityManager
基本上意味着同一事务中希望使用 an 的两个对象EntityManager
都将看到相同的EntityManager
内容,并且无需显式传递EntityManager
。所有这些“传递”都是由容器为你完成的。
这是如何实现的?很简单,EntityManager
你从容器中得到的是假货。这是一个包装。当您使用它时,它会在当前事务中查找 realEntityManager
并将调用委托给EntityManager
. 这就是神秘EntityManager.getDelegate()
方法的原因,因此用户可以根据需要获取真正的EntityManager 并使用任何非标准 API。这样做当然要非常小心,永远不要保留对委托的引用,EntityManager
否则您将遇到严重的内存泄漏。当事务完成时,委托EntityManager
通常会被刷新、关闭、清理和丢弃。如果您仍然持有一个引用,您将阻止对该引用EntityManager
以及它所持有的所有数据进行垃圾收集。
EntityManager
保存对从容器中获得的a 的引用总是安全的
- 引用它是不安全的
EntityManager.getDelegate()
- 要非常小心地引用
EntityManager
您通过 an 创建的自己的引用EntityManagerFactory
——您对它的管理负有 100% 的责任。
CDI 集成
我不想过度简化 CDI,但我发现它有点太大了,而且很多人没有认真看 - 它在很多人的“总有一天”列表上:) 所以这里只是几个亮点我想一个“网络人”会想知道。
你知道在一个典型的 web 应用程序中的所有投入和获取吗?HttpSession
整天把东西拉进拉出?使用String
你从HttpSession
. 您可能已经使用实用程序代码为您执行此操作。
CDI 也有这个实用程序代码,它被称为@SessionScoped
. 任何带有注释的对象@SessionScoped
都会被放入并跟踪HttpSession
。您只需通过请求将对象注入到您的 Servlet 中@Inject FooObject
,CDI 容器就会以与我描述的事务跟踪相同的方式跟踪“真实”FooObject 实例EntitityManager
。Abracadabra,现在你可以删除一堆代码了 :)
做任何getAttribute
事setAttribute
吗HttpServletRequest
?好吧,你也可以用@RequestScoped
同样的方法删除它。
当然还有@ApplicationScoped
消除你可能正在做的getAttribute
电话setAttribute
ServletContext
为了让事情变得更酷,任何像这样跟踪的对象都可以实现一个@PostConstruct
在创建 bean 时调用的@PreDestroy
方法,以及在所述“范围”完成时通知的方法(会话完成,请求结束,应用程序正在关闭)向下)。
CDI 可以做得更多,但这足以让任何人想要重写旧的 web 应用程序。
一些挑剔的东西
Java EE 6 中添加了一些在 Tomcat 驾驶室中没有添加的东西。它们不需要大的解释,但确实占了“填补空白”的很大一部分。
- 支持
@DataSourceDefinition
- 支持全球 JNDI (
java:global
, java:app
, java:module
)
@Resource MyEnum myEnum
通过和枚举注入
@Resource Class myPluggableClass
通过and进行类注入
- 支持
@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 配置文件中删除,则剩下的内容如下:
- Java 小服务程序
- Java 服务器页面 (JSP)
- Java ServerFaces (JSF)
- Java 事务 API (JTA)
- Java 持久性 API (JPA)
- Java 上下文和依赖注入 (CDI)
- Bean 验证
这是一个非常有用的堆栈。