Dispatcher-servlet无法映射到websocket请求

时间:2016-03-15 13:06:00

标签: java xml spring websocket applicationcontext

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

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

<websocket:message-broker>
    <websocket:stomp-endpoint path="/stomp">
        <websocket:sockjs/>
    </websocket:stomp-endpoint>
    <websocket:simple-broker prefix="/topic,/queue"/>
</websocket:message-broker>

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

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

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

此问题与Spring : how to expose SimpMessagingTemplate bean to root context ?相同,但从另一个角度看(在根上下文中声明websocket而不是在dispatcher-servlet中)

2 个答案:

答案 0 :(得分:1)

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

脏修复是Autowire控制器和预定类中的SimpMessagingTemplate(全部由dispatcher-servlet扫描,其中websocket tag已声明),并通过SimpMessagingTemplate作为服务方法的参数(在root context中声明)。

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

答案 1 :(得分:0)

在编写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<ContextRefreshedEvent> {

@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<FieldWithAnnotation<Autowired>> fields = ReflectionUtils.findFieldsWithAnnotation(clazz, Autowired.class);
            for (FieldWithAnnotation<Autowired> fieldWithAnno : fields) {
                Field field = fieldWithAnno.getField();
                if(field.getType() == SimpMessagingTemplate.class){
                    field.setAccessible(true);
                    try {
                        field.set(bean, template);
                    } catch (Exception e) {}
                }
            }

            List<Method> 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();
    }
}
}