实际上,我认为监听JMS可能是应用服务器的最佳原因。独立的消息代理并不能解决问题,因为您仍然需要一个组件来侦听消息。最好的方法是使用MDB。理论上,您可以使用Spring的MessageListenerContainer。但是,这有几个缺点,例如JMS仅支持阻塞读取,因此Spring需要启动自己的线程,这是完全不受支持的(即使在Tomcat中),可能会破坏事务、安全性、命名(JNDI)和类加载(这反过来可能会破坏远程访问)。JCA资源适配器可以自由地执行任何操作,包括通过WorkManager旋转线程。很可能除了JMS(或其他目标)之外还使用了数据库,在这种情况下,您需要XA事务和JTA,换句话说,需要应用服务器。是的,您可以将其打补丁到servlet容器中,但在这一点上,它变得与应用服务器无法区分。
在我看来,反对应用服务器的最大原因是,在规范发布后需要几年时间(这也需要几年时间),直到服务器实现规范并消除了最严重的错误。直到现在,即将发布EE 7之前,我们才有EE 6服务器开始出现,而这些服务器并不完全充斥着错误。有时候甚至变得滑稽,一些供应商不再修复他们EE 6产品线上的错误,因为他们已经忙于即将推出的EE 7系列产品。
编辑:
最后一段的详细解释:
在很多地方,Java EE依赖于所谓的上下文信息。这些信息不是从服务器/容器显式传递给应用程序的参数,而是隐含地“存在”。例如,当前用户用于安全检查。当前事务或连接。当前应用程序用于查找类以惰性加载代码或反序列化对象。或者当前组件(servlet、EJB等)用于执行JNDI查找。所有这些信息都在服务器/容器调用组件(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 异步处理
仅当请求被通过AsyncContext.dispatch方法分派到容器时,才能为执行初始请求的线程提供Java Enterprise Edition功能,例如第15.2.2节“Web应用程序环境”(第15-174页)和第15.3.1节“在EJBTM调用中传播安全标识”(第15-176页)。通过AsyncContext.start(Runnable)方法直接操作响应对象的其他线程可能会获得Java Enterprise Edition功能。
这是关于异步处理的内容,但是对于自定义线程也适用相同的限制。
public void start(Runnable r) - 此方法导致容器分派一个线程(可能来自受控线程池)来运行指定的Runnable。容器可以将适当的上下文信息传播给Runnable。
同样,这是关于异步处理的内容,但是对于自定义线程也适用相同的限制。
15.2.2 Web应用环境
这种类型的servlet容器应该支持开发人员创建的线程执行此行为,但目前并不要求。在下一个版本的规范中将添加此要求。开发人员应注意,依赖于此能力来创建应用程序线程是不推荐的,因为它是不可移植的。
不可移植意味着它可能在一个服务器上运行,但在另一个服务器上可能无法运行。
当您想要在MDB之外使用JMS接收消息时,可以在javax.jms.MessageConsumer
上使用四种方法:
#receiveNoWait()
方法可以在容器线程中使用,它不会阻塞,但类似于窥视。如果没有消息,则返回null
。这不太适合用于监听消息。
#receive(long)
方法可以在容器线程中使用,它会阻塞。通常不建议在容器线程中进行阻塞等待。同样不太适合用于监听消息。
#receive()
方法可能会无限期地阻塞。同样不太适合用于监听消息。
#setMessageListener()
方法是你想要的,当消息到达时会得到回调。然而,除非库能够钩入应用服务器,否则这不会是一个容器线程。钩子只能通过JCA连接到资源适配器来获取应用服务器的访问权限。
所以,是的,它可能起作用,但不能保证,并且有很多可能会出现问题的情况。