java - ejemplo - Dispatcher-servlet no se puede asignar a las solicitudes de websocket
websocket service java (2)
Encontré una solución sucia. No me gusta, pero dada la falta de respuestas en SO, así como de colegas actuales y anteriores, tuve que seguir adelante con el proyecto e implementar una solución sucia.
La solución sucia es Autowire
SimpMessagingTemplate
en Controller y las clases Programadas (todas escaneadas por el dispatcher-servlet
, donde se declara la websocket tag
), y pasar SimpMessagingTemplate
como un parámetro a los métodos de servicio (declarados en el root context
).
Esta solución no es transparente (la SimpMessagingTemplate
debe autoconectar directamente en los servicios, idealmente) pero definitivamente soluciona el problema.
Estoy desarrollando una aplicación web Java con Spring como marco principal (Spring core, Spring mvc, Spring security, Spring data, Spring websocket son notablemente utilizados).
La declaración de un intermediario de mensajes en un contexto de Spring como este proporciona un bean SimpMessagingTemplate para el contexto:
<websocket:message-broker>
<websocket:stomp-endpoint path="/stomp">
<websocket:sockjs/>
</websocket:stomp-endpoint>
<websocket:simple-broker prefix="/topic,/queue"/>
</websocket:message-broker>
Tengo que poner esta etiqueta en mi contexto raíz (applicationContext.xml), de lo contrario los servicios declarados en ese contexto raíz no pueden enviar notificaciones a los usuarios a través de websocket (porque necesitan SimpMessagingTemplate).
La cuestión es que si pongo esta etiqueta en el contexto raíz, los clientes obtienen un 404 cuando se suscriben a websocket. Y si pongo la etiqueta en el servlet despachador, los servicios en el contexto raíz no pueden enviar notificaciones, ya que necesitarían SimpMessagingTemplate (pero solo está disponible en el contexto del servlet de distribución hijo).
¿Hay alguna manera de "enlazar" el servlet despachador con el intermediario? Declarar el frijol dos veces no es una solución correcta.
Este problema es el mismo que en Spring: ¿cómo exponer el bean SimpMessagingTemplate al contexto raíz? pero mirando desde otro ángulo (declarando websocket en el contexto raíz en lugar de en el servlet dispatcher)
Escribí un bean para hacer la inyección después del contexto de la aplicación servlet. Buscará a través de los contextos de la aplicación padre para inyectar SimpMessageTemplate
Cualquier bean que necesite la plantilla:
@Autowired(required=false) //required=false so that it won''t throw Exception when startup
private SimpMessagingTemplate messagingTemplate;
PostInjectSimpMessageTemplateBean:
Coloque este bean en el contexto de la aplicación de servlet (es decir, el mismo archivo xml que el websocket ubicado)
( Reemplazar "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();
}
}
}