Spring:如何将 SimpMessagingTemplate bean 暴露给根上下文?
Spring : how to expose SimpMessagingTemplate bean to root context ?
我正在开发一个 Java webapp 以 Spring 作为主要框架(Spring 核心,Spring mvc,Spring 安全,Spring 数据,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 上有几篇文章描述这个常见错误,通常用户希望获得所有主体的列表,但由于 SessionRegistry
bean 重复而得到空列表,并找出这一点)。
所以为了解决这个问题,我删除了
<import resource="applicationContext.xml"/>
来自调度员-servlet.xml,但从那以后:
- 要么我将
<websocket:messagebroker>...</>
标签放在调度程序中-servlet.xml,在这种情况下,与 websocket 的连接成功但服务无法自动装配 SimpMessagingTemplate
- 或者我将
<websocket:messagebroker>...</>
标记放在 applicationContext.xml
中,在这种情况下客户端无法连接到 websocket。
- (或者我回到以前的版本,其中
DispatcherServlet
导入 ApplicationContext
,这会破坏 SessionRegistry
- 不)
这个可能相当普遍的问题的解决方案是什么? DispatcherServlet
可以从根上下文访问 bean,但反之亦然,那么我应该如何解决这个问题?
我找到了一个肮脏的解决方案。我不喜欢它,但鉴于缺少关于 SO 的答案(另请参阅: ),以及来自现任和前任同事的答案,我不得不继续该项目并实施了一个肮脏的修复。
肮脏的修复是 Autowire
Controller 和 Scheduled 类 中的 SimpMessagingTemplate
(全部由 dispatcher-servlet
扫描,其中声明 websocket tag
),并将 SimpMessagingTemplate
作为参数传递给服务方法(在 root context
中声明)。
此解决方案不透明(理想情况下 SimpMessagingTemplate
应该直接在服务中自动装配)但它确实解决了问题。
我正在开发一个 Java webapp 以 Spring 作为主要框架(Spring 核心,Spring mvc,Spring 安全,Spring 数据,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 上有几篇文章描述这个常见错误,通常用户希望获得所有主体的列表,但由于 SessionRegistry
bean 重复而得到空列表,并找出这一点)。
所以为了解决这个问题,我删除了
<import resource="applicationContext.xml"/>
来自调度员-servlet.xml,但从那以后:
- 要么我将
<websocket:messagebroker>...</>
标签放在调度程序中-servlet.xml,在这种情况下,与 websocket 的连接成功但服务无法自动装配SimpMessagingTemplate
- 或者我将
<websocket:messagebroker>...</>
标记放在applicationContext.xml
中,在这种情况下客户端无法连接到 websocket。 - (或者我回到以前的版本,其中
DispatcherServlet
导入ApplicationContext
,这会破坏SessionRegistry
- 不)
这个可能相当普遍的问题的解决方案是什么? DispatcherServlet
可以从根上下文访问 bean,但反之亦然,那么我应该如何解决这个问题?
我找到了一个肮脏的解决方案。我不喜欢它,但鉴于缺少关于 SO 的答案(另请参阅:
肮脏的修复是 Autowire
Controller 和 Scheduled 类 中的 SimpMessagingTemplate
(全部由 dispatcher-servlet
扫描,其中声明 websocket tag
),并将 SimpMessagingTemplate
作为参数传递给服务方法(在 root context
中声明)。
此解决方案不透明(理想情况下 SimpMessagingTemplate
应该直接在服务中自动装配)但它确实解决了问题。