Диспетчер-сервлет не может сопоставить запросы websocket


Я занимаюсь разработкой на Java веб-приложения с весны, как главной основы (сердечника весны, Весна в MVC, Весна безопасности, весенний данные, Весна WebSocket, которая явно используется).

Объявление брокера сообщений в контексте Spring, как это, предоставляет Боб SimpMessagingTemplate контексту:

<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).

Дело в том, что если я помещаю этот тег в корневой контекст, клиенты получают 404, когда они подписываются на websocket. И если я помещу тег в dispatcher-servlet, то службы в корневом контексте не смогут отправлять уведомления, поскольку им потребуется SimpMessagingTemplate (но он доступен только в дочернем контексте dispatcher-servlet).

Есть ли способ "привязать" диспетчер-сервлет к брокеру ? Объявление Боба дважды не является правильным решение.

Эта проблема такая же, как и Spring : как предоставить simpmessagingtemplate bean корневому контексту ? но с другой стороны (объявление websocket в корневом контексте, а не в dispatcher-servlet)

2 4

2 ответа:

Я нашел грязное решение. Мне это не нравится,но учитывая отсутствие ответов на SO, а также от нынешних и бывших коллег, я должен был идти вперед с проектом и реализовал грязное исправление.

Грязное исправление состоит в том, чтобы Autowire SimpMessagingTemplate в контроллере и запланированных классах (все сканируются dispatcher-servlet, где объявлен websocket tag), и передать SimpMessagingTemplate в качестве параметра в методы обслуживания (объявленные в root context).

Это решение непрозрачно (SimpMessagingTemplate должно быть автоматически подключено непосредственно в сервисах в идеале), но это определенно исправляет проблему.

Я написал Боб, чтобы сделать инъекцию после контекста приложения сервлета inited. Он будет искать в контекстах родительского приложения, чтобы ввести SimpMessageTemplate

Любой Боб, которому нужен шаблон:

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

PostInjectSimpMessageTemplateBean:

Поместите этот боб в контекст приложения сервлета (т. е. тот же xml-файл, который находится в websocket)

(заменить "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();
    }
}
}