@MessageMapping 带占位符

@MessageMapping with placeholders

我正在使用 Spring-websocket,但遇到以下问题:

我试图在 @MessageMapping 注释中放置一个占位符,以便从属性中获取 url。它适用于 @RequestMapping 但不适用于 @MessageMapping.

如果我使用此占位符,则 URL 为空。有什么想法或建议吗?

示例:

@RequestMapping(value= "${myProperty}")

@MessageMapping("${myProperty}")

更新 :

现在我明白你的意思了,但我认为那是不可能的(还)。

Documentation 没有提及任何与路径映射 URI 相关的内容。

旧答案

使用

   @MessageMapping("/handler/{myProperty}")

而不是

   @MessageMapping("/handler/${myProperty}")

并像这样使用它:

   @MessageMapping("/myHandler/{username}")
   public void handleTextMessage(@DestinationVariable String username,Message message) {
        //do something
   }

Spring 允许您在 @RequestMapping, but not in @MessageMapping 中使用 属性 占位符。这是因为 MessageHandler。因此,我们需要覆盖默认值 MessageHandler 来执行此操作。

WebSocketAnnotationMethodMessageHandler 不支持占位符,您需要自己添加此支持。

为简单起见,我刚刚在我的项目中创建了另一个 WebSocketAnnotationMethodMessageHandler class 与原始 org.springframework.web.socket.messaging 相同的包,并覆盖 [=26] 中的 getMappingForMethod 方法=] 具有相同的内容,仅更改 SimpMessageMappingInfo 的构造方式(private in WebSocketAnnotationMethodMessageHandler):

private SimpMessageMappingInfo createMessageMappingCondition(final MessageMapping annotation) {
    return new SimpMessageMappingInfo(SimpMessageTypeMessageCondition.MESSAGE, new DestinationPatternsMessageCondition(
            this.resolveAnnotationValues(annotation.value()), this.getPathMatcher()));
}

private SimpMessageMappingInfo createSubscribeCondition(final SubscribeMapping annotation) {
    final SimpMessageTypeMessageCondition messageTypeMessageCondition = SimpMessageTypeMessageCondition.SUBSCRIBE;
    return new SimpMessageMappingInfo(messageTypeMessageCondition, new DestinationPatternsMessageCondition(
            this.resolveAnnotationValues(annotation.value()), this.getPathMatcher()));
}

这些方法现在将根据属性解析值(调用 resolveAnnotationValues 方法),因此我们需要使用如下内容:

private String[] resolveAnnotationValues(final String[] destinationNames) {
    final int length = destinationNames.length;
    final String[] result = new String[length];

    for (int i = 0; i < length; i++) {
        result[i] = this.resolveAnnotationValue(destinationNames[i]);
    }

    return result;
}

private String resolveAnnotationValue(final String name) {
    if (!(this.getApplicationContext() instanceof ConfigurableApplicationContext)) {
        return name;
    }

    final ConfigurableApplicationContext applicationContext = (ConfigurableApplicationContext) this.getApplicationContext();
    final ConfigurableBeanFactory configurableBeanFactory = applicationContext.getBeanFactory();

    final String placeholdersResolved = configurableBeanFactory.resolveEmbeddedValue(name);
    final BeanExpressionResolver exprResolver = configurableBeanFactory.getBeanExpressionResolver();
    if (exprResolver == null) {
        return name;
    }
    final Object result = exprResolver.evaluate(placeholdersResolved, new BeanExpressionContext(configurableBeanFactory, null));
    return result != null ? result.toString() : name;
}

您仍然需要在配置中定义一个 PropertySourcesPlaceholderConfigurer bean。

如果您使用的是基于 XML 的配置,请包含如下内容:

<context:property-placeholder location="classpath:/META-INF/spring/url-mapping-config.properties" />

如果你使用的是基于Java的配置,你可以这样尝试:

@Configuration
@PropertySources(value = @PropertySource("classpath:/META-INF/spring/url-mapping-config.properties"))
public class URLMappingConfig {

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }

}

Obs.:在这种情况下,url-mapping-config.properties 文件位于 src\main\resources\META-INF\spring 文件夹中的 gradle/maven 项目中,内容如下所示:

myPropertyWS=urlvaluews

这是我的示例控制器:

@Controller
public class WebSocketController {

    @SendTo("/topic/test")
    @MessageMapping("${myPropertyWS}")
    public String test() throws Exception {
        Thread.sleep(4000); // simulated delay
        return "OK";
    }

}

默认情况下 MessageHandler 启动日志将打印如下内容:

INFO: Mapped "{[/${myPropertyWS}],messageType=[MESSAGE]}" onto public java.lang.String com.brunocesar.controller.WebSocketController.test() throws java.lang.Exception

然后用我们的 MessageHandler 现在打印这个:

INFO: Mapped "{[/urlvaluews],messageType=[MESSAGE]}" onto public java.lang.String com.brunocesar.controller.WebSocketController.test() throws java.lang.Exception

在此 gist 中查看完整的 WebSocketAnnotationMethodMessageHandler 实施。

编辑: 此解决方案解决了 4.2 GA 之前版本的问题。有关详细信息,请参阅 this jira。

Rossen Stoyanchev 添加了对@MessageMapping 和@SubscribeMapping 方法的占位符支持。

查看 Jira 问题:https://jira.spring.io/browse/SPR-13271

@MessageMapping("/chat/{roomId}")
public Message handleMessages(@DestinationVariable("roomId") String roomId, @Payload Message message, Traveler traveler) throws Exception {
    System.out.println("Message received for room: " + roomId);
    System.out.println("User: " + traveler.toString());

    // store message in database
    message.setAuthor(traveler);
    message.setChatRoomId(Integer.parseInt(roomId));
    int id = MessageRepository.getInstance().save(message);
    message.setId(id);

    return message;
}