@MessageMapping带有占位符

4

我正在使用Spring-Websocket,遇到了以下问题:

我试图在@MessageMapping注释中放置一个占位符,以便从属性中获取URL。它在@RequestMapping中有效,但在@MessageMapping中无效。

如果我使用这个占位符,URL是空的。有什么想法或建议吗?

例如:

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

@MessageMapping("${myProperty}")
4个回答

2

1
Spring允许你在 @RequestMapping 中使用属性占位符,但在@MessageMapping中不行。这是因为MessageHandler。因此,我们需要重写默认的MessageHandler来实现这一点。 WebSocketAnnotationMethodMessageHandler不支持占位符,您需要自己添加此功能。
为了简单起见,我在我的项目中创建了另一个WebSocketAnnotationMethodMessageHandler类,它位于与原始类相同的包org.springframework.web.socket.messaging中,并重写SimpAnnotationMethodMessageHandler中的getMappingForMethod方法,内容相同,只是改变了使用此方法构造SimpMessageMappingInfo的方式(在WebSocketAnnotationMethodMessageHandler中为private)。
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();
    }

}

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

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。

它适用于@requedtmapping,但你尝试过@Messagemapping吗? - Karthik
1
@bruno_cesar 你的第一个回答吓到我了 :P我已经尝试了一个小时的4.2.0.RC1 :P - Karthik
@karthik 是的,抱歉 :P - Bruno Ribeiro
没有问题 :) - Karthik
2
嗨,布鲁诺,我在Jira中添加了一个问题,他们已经将改进包含在4.2中。感谢您的评论。 - crm86

0

更新:

现在我明白你的意思了,但我认为这还不可能。

文档中没有提到与路径映射URI相关的任何内容。

enter image description here

旧回答

使用

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

而不是

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

然后像这样使用:

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

这不是设置URL,这只是传递参数。我想通过属性指定URL,我的意思是${myProperty}表示完整的URL。 - crm86

-1
@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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接