Dispatcher-servlet无法映射到websocket请求

我正在开发一个以Web为主框架的Java webapp(Spring核心,Spring mvc,Spring安全,Spring数据,Spring websocket特别使用)。

在这样的Spring上下文中声明一个消息代理,为上下文提供了一个SimpMessagingTemplate bean:

      

我必须将此标记放在我的根上下文(applicationContext.xml)中,否则在该根上下文中声明的服务不能通过websocket向用户发送通知(因为它们需要SimpMessagingTemplate)。

问题是,如果我将此标记放在根上下文中,客户端在订阅websocket时会获得404。 如果我将标记放在dispatcher-servlet中,那么根上下文中的服务就不能发送通知,因为它们需要SimpMessagingTemplate(但它只能在子dispatcher-servlet上下文中使用)。

有没有办法将调度程序-servlet“绑定”到代理? 两次声明bean不是一个正确的解决方案。

这个问题和Spring一样:如何将SimpMessagingTemplate bean暴露给root上下文? 但从另一个角度看(在根上下文中而不是在dispatcher-servlet中声明websocket)

我发现了一个肮脏的解 我不喜欢它,但考虑到SO以及现任和前任同事缺乏答案,我不得不继续进行项目并实施一个肮脏的修复。

脏修复是在Controller和Scheduled类中自动SimpMessagingTemplate (所有都由dispatcher-servlet扫描,其中声明了websocket tag ),并将SimpMessagingTemplate作为参数传递给服务方法(在root context声明)。

这个解决方案不透明(理想情况下, SimpMessagingTemplate应直接在服务中自动连接)但它肯定能解决问题。

在编写servlet应用程序上下文后,我编写了一个bean来执行注入。 它将搜索父应用程序上下文以注入SimpMessageTemplate

无论需要模板的bean是什么:

 @Autowired(required=false) //required=false so that it won't throw Exception when startup private SimpMessagingTemplate messagingTemplate; 

PostInjectSimpMessageTemplateBean:

将此bean放在servlet应用程序上下文中(即,websocket所在的xml文件)

替换“YOUR.PACKAGE.NAME”

 public class PostInjectSimpMessageTemplateBean implements ApplicationListener { @Override public void onApplicationEvent(ContextRefreshedEvent event) { ApplicationContext servletContext = event.getApplicationContext(); ApplicationContext context = servletContext.getParent(); SimpMessagingTemplate template = servletContext.getBean(SimpMessagingTemplate.class); while(context != null){ for(String beanName : context.getBeanDefinitionNames()){ Object bean = context.getBean(beanName); Class clazz = bean.getClass(); if(!clazz.getName().startsWith("YOUR.PACKAGE.NAME")) continue; List> fields = ReflectionUtils.findFieldsWithAnnotation(clazz, Autowired.class); for (FieldWithAnnotation fieldWithAnno : fields) { Field field = fieldWithAnno.getField(); if(field.getType() == SimpMessagingTemplate.class){ field.setAccessible(true); try { field.set(bean, template); } catch (Exception e) {} } } List methods = ReflectionUtils.findMethodsWithAnnotation(clazz, Autowired.class); for (Method method : methods) { Class[] paramtypes = method.getParameterTypes(); if(paramtypes.length == 1){ if(paramtypes[0] == SimpMessagingTemplate.class){ method.setAccessible(true); try { method.invoke(bean, template); } catch (Exception e) {} } } } } context = context.getParent(); } } }