实际上我会说监听 JMS 可能是应用服务器的最佳理由。独立的消息代理不能解决问题,因为您仍然需要一个正在侦听消息的组件。最好的方法是使用 MDB。理论上你可以使用 Springs MessageListenerContainer。然而,这有几个缺点,例如 JMS 仅支持阻塞读取,因此 Spring 需要启动它自己的线程,这完全不受支持(即使在 Tomcat 中),并且可能会破坏事务、安全性、命名(JNDI)和类加载(这反过来会破坏远程)。JCA 资源适配器可以随意做任何事情,包括通过 WorkManager 启动线程。除了 JMS(或其他目的地)之外,可能还使用了一个数据库,此时您需要 XA 事务和 JTA,换句话说,一个应用程序服务器。
恕我直言,反对应用服务器的最大原因是规范发布后需要数年时间(这反过来也需要数年时间),直到服务器实施规范并消除最严重的错误。直到现在,就在 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-176 页的第 15.3.1 节“在 EJBTM 调用中传播安全身份”)仅适用于执行初始请求的线程或者当请求通过 AsyncContext.dispatch 方法分派到容器时。通过 AsyncContext.start(Runnable) 方法直接在响应对象上运行的其他线程可以使用 Java 企业版功能。
这是关于异步处理的,但同样的限制也适用于自定义线程。
public void start(Runnable r) - 此方法使容器调度一个线程,可能来自托管线程池,以运行指定的 Runnable。容器可以将适当的上下文信息传播到 Runnable。
同样,异步处理,但同样的限制适用于自定义线程。
15.2.2 Web应用环境
这种类型的 servlet 容器在开发人员创建的线程上执行时应该支持这种行为,但目前不需要这样做。这样的要求将在本规范的下一个版本中添加。开发人员需要注意的是,不建议依赖于应用程序创建的线程的此功能,因为它是不可移植的。
不可移植意味着它可以在一台服务器上但不能在另一台服务器上。
当您想在 MDB 之外使用 JMS 接收消息时,您可以使用以下四种方法javax.jms.MessageConsumer
:
#receiveNoWait()
您可以在容器线程中执行此操作,它不会阻塞,但就像偷看一样。如果没有消息,它只会返回null
。这不太适合收听消息。
#receive(long)
您可以在容器线程中对此进行处理,它确实会阻塞。您通常不希望在容器线程中进行阻塞等待。再次不太适合收听消息。
#receive()
,这可能会无限期地阻塞。再次不太适合收听消息。
#setMessageListener()
这就是你想要的,当消息到达时你会得到一个回调。但是,除非库可以连接到应用程序服务器,否则它不会是容器线程。应用程序服务器的挂钩只能通过 JCA 提供给资源适配器。
所以是的,它可能会起作用,但不能保证,并且有很多事情可能会破坏。