我正在开发一个以Spring为主要框架的Java webapp(Spring core,Spring mvc,Spring security,Spring data,Spring websocket被特别使用)。
像这样在 Spring 上下文中声明message-broker
为上下文提供了一个SimpMessagingTemplate
bean:
<websocket:message-broker>
<websocket:stomp-endpoint path="/stomp">
<websocket:sockjs/>
</websocket:stomp-endpoint>
<websocket:simple-broker prefix="/topic,/queue"/>
</websocket:message-broker>
我必须将此标签放在dispatcher-servlet.xml
(而不是applicationContext.xml
),否则客户端在尝试连接到 websocket 时(在初始页面加载时)将收到 404。
但是,由于提供SimpMessagingTemplate
Bean(用于将消息发送到连接的客户端)的标记在根上下文中不可用,因此当服务(由根上下文扫描)发送 websocket 消息时,SimpMessagingTemplate
Bean 无法自动连接(经典NoSuchBeanDefinitionException
)。
以前,<websocket:message-broker>
标签在applicationContext.xml
中,dispatcher-servlet.xml
正在导入applicationContext.xml
并且一切正常 - 但是当我最近使用SessionRegistry
修改任意用户会话时,我惊讶地发现这是错误的。
事实上,由于DispatcherServlet
显式导入了已经隐式继承的根上下文,因此SessionRegistry
bean 被创建了两次,导致了意想不到的行为(SO 上有几个帖子描述了这个常见的错误,通常用户希望获得所有主体的列表,但由于 bean 复制SessionRegistry
而获得空列表并了解这一点)。
所以为了解决这个问题,我删除了
<import resource="applicationContext.xml"/>
从调度程序-servlet.xml,但从那时起:
- 要么我将
<websocket:messagebroker>...</>
标签放在调度程序-servlet 中.xml,在这种情况下,与 websocket 的连接成功,但服务无法自动连线SimpMessagingTemplate
- 或者我把
<websocket:messagebroker>...</>
标签放在applicationContext.xml
,在这种情况下,客户端无法连接到websocket。 - (或者我回到以前的版本,
DispatcherServlet
导入ApplicationContext
,这打破了SessionRegistry
- 不)
这个可能相当普遍的问题的解决方案是什么?DispatcherServlet
可以从根上下文访问 bean,但反之则不然,那么我应该如何解决这个问题呢?
我找到了一个肮脏的解决方案。我不喜欢它,但鉴于 SO 缺乏答案(另请参阅:Dispatcher-servlet 无法映射到 websocket 请求),以及现任和前任同事,我不得不继续该项目并实施一个肮脏的修复。
脏修复是Autowire
控制器和调度类中的SimpMessagingTemplate
(全部由声明websocket tag
的dispatcher-servlet
扫描),并将SimpMessagingTemplate
作为参数传递给服务方法(在root context
中声明)。
此解决方案不透明(理想情况下,SimpMessagingTemplate
应直接在服务中自动连接),但它绝对可以解决问题。