实际上,我会说监听 JMS 可能是使用应用程序服务器的最佳理由。独立的消息代理不能解决此问题,因为您仍然需要一个侦听消息的组件。最好的方法是使用MDB。从理论上讲,你可以使用Spring MessageListenerContainer。然而,这有几个缺点,比如JMS只支持阻塞读取,因此Spring需要启动它自己的线程,这是完全不支持的(即使在Tomcat中),并且可以破坏事务,安全性,命名(JNDI)和类加载(反过来可以破坏远程处理)。JCA 资源适配器可以自由地做任何它想做的事情,包括通过 WorkManager 启动线程。除了 JMS(或其他目标)之外,可能还使用了一个数据库,此时您需要 XA 事务和 JTA,换句话说,就是应用程序服务器。是的,您可以将其修补到 servlet 容器中,但是此时它与应用程序服务器无法区分。
恕我直言,反对应用程序服务器的最大原因是,在规范发布后需要数年时间(反过来也需要数年时间),直到severs实现该规范并解决最严重的错误。直到现在,在EE 7即将发布之前,我们才有EE 6服务器开始出现,这些服务器并没有完全充满错误。这变得滑稽到一些供应商不再修复其EE 6系列中的错误,因为他们已经忙于即将推出的EE 7系列。
编辑
最后一段的长篇解释:
Java EE在很多地方都依赖于所谓的上下文信息。未作为参数从服务器/容器显式传递到应用程序但隐式“在那里”的信息。例如,用于安全检查的当前用户。当前事务或连接。用于查找类以延迟加载代码或反序列化对象的当前应用程序。或者用于执行 JNDI 查找的当前组件(servlet、EJB 等)。所有这些信息都在服务器/容器在调用组件(servlet、EJB 等)之前设置的线程局部变量中。如果您创建自己的线程,那么服务器/容器就不知道它们,并且依赖于此信息的所有功能都不再起作用。您可以通过在您生成的线程中不使用任何这些功能来逃脱这一点。
一些链接
http://www.oracle.com/technetwork/java/restrictions-142267.html#threads
http://www.ibm.com/developerworks/websphere/techjournal/0609_alcott/0609_alcott.html#spring-4
如果我们检查 Servlet 3.0 规范,我们会发现:
2.3.3.3 异步处理
Java Enterprise Edition 功能,如第 15-174 页上的第 15.2.2 节 “Web 应用程序环境”和第 15.3.1 节 “在 EJBTM 调用中传播安全身份”(第 15-176 页)等功能,仅对执行初始请求的线程或通过 AsyncContext.dispatch 方法将请求分派到容器时可用。Java Enterprise Edition 功能可能可用于通过 AsyncContext.start(Runnable) 方法直接在响应对象上运行的其他线程。
这是关于异步处理的,但相同的限制也适用于自定义线程。
public void start(Runnable r) - 此方法使容器调度一个线程(可能来自托管线程池)来运行指定的 Runnable。容器可以将适当的上下文信息传播到 Runnable。
同样,异步处理但相同的限制也适用于自定义线程。
15.2.2 Web 应用程序环境
这种类型的 servlet 容器在开发人员创建的线程上执行时应支持此行为,但当前不需要这样做。此规范的下一个版本将添加此类要求。请开发人员注意,不建议将此功能用于应用程序创建的线程,因为它是不可移植的。
不可移植意味着它可以在一台服务器中,但不能在另一台服务器中。
当您希望在MDB之外使用JMS接收消息时,您可以使用以下四种方法:javax.jms.MessageConsumer
-
#receiveNoWait()
你可以在容器线程中做到这一点,它不会阻塞,但就像偷看一样。如果没有消息存在,则只返回 。这不太适合收听消息。null
-
#receive(long)
你可以在容器线程中做到这一点,它确实会阻塞。您通常不会不想在容器线程中执行阻塞等待。同样不是很好适合听消息。
-
#receive()
,这可能会无限期地阻塞。同样不是很好适合听消息。
-
#setMessageListener()
这是你想要的,当消息到达时,你会得到一个回调。但是,除非库可以挂接到应用程序服务器,否则这不会是容器线程。与应用程序服务器的挂钩只能通过 JCA 连接到资源适配器。
所以,是的,它可能有效,但它不能保证,有很多东西可能会坏掉。