@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;
}
我正在使用 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;
}