Spring框架提供基于websocket的STOMP支持,需要使用spring-messaging
和spring-websocket
模块。
下面的配置中,注册了一个前缀为/portfolio
的stomp终端,客户端可以使用该url来建立websocket连接。
Message的destination如果是以/app
开头,则会转发给响应的消息处理方法(如使用@MessageMapping注解的方法),如果是以/topic
,/queue
开头则会被转发给消息代理(broker),由broker广播给连接的客户端。
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/portfolio").withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.setApplicationDestinationPrefixes("/app");//这个前缀不需要与web项目名相同,可以自己随意指定
config.enableSimpleBroker("/topic", "/queue");//这里配置了两个前缀,若是destination以这两个前缀开头,则会转发给该Broker
}
}
或者使用XML
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
http://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:message-broker application-destination-prefix="/app">
<websocket:stomp-endpoint path="/portfolio">
<websocket:sockjs/>
websocket:stomp-endpoint>
<websocket:simple-broker prefix="/topic, /queue"/>
websocket:message-broker>
beans>
这里使用js作为stomp的客户端,需要使用stomp.js和sockjs-client,/spring-websocket-portfolio
是web app的项目名,/portfolio
是stomp终端名
var socket = new SockJS("/spring-websocket-portfolio/portfolio");
var stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
}
若不适用sockjs,则可以使用原生的websocket api
var socket = new WebSocket("/spring-websocket-portfolio/portfolio");
var stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
}
Message在应用中的流动是这样一个流程,如上图。若destination是以/app开始则会通过request channel交给注解方法来处理,处理完毕根据默认的路径转发给SimpleBroker处理(若不使用默认路径可以用@SendTo来指定路径),处理完毕后交由response channel返回连接的客户端。
若destination是以/topic开头则直接交给SimpleBroker处理。
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/portfolio");
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
registry.enableSimpleBroker("/topic");
}
}
@Controller
public class GreetingController {
@MessageMapping("/greeting")
@SendTo("/topic/greetings")
public String handle(String greeting) {
return "[" + getTimestamp() + ": " + greeting;
}
@SubscribeMapping("/init")
public String init(){
return "init";
}
}
使用@SubscribeMapping注解,处理client的subscribe请求,被注解的方法将直接返回一个信息给连接的client,不会经过Broker.常用于进行初始化操作。若是在该方法上使用了@SendTo,则会转发至broker处理。
上面我们介绍了如何在controller中发送数据给clients,spring还提供了一个叫做SimpMessagingTemplate的模板,以便我们在应用的任何地方发送数据给Broker
@Controller
public class GreetingController {
@Autowired
private SimpMessagingTemplate template;
@RequestMapping(path="/greetings", method=POST)
public void greet(String greeting) {
String text = "[" + getTimestamp() + "]:" + greeting;
template.convertAndSend("/topic/greetings", text);
}
}
@Service
public class GreetingServiceImpl {
@Autowired
private SimpMessagingTemplate template;
public void greet(String greeting) {
String text = "[" + getTimestamp() + "]:" + greeting;
template.convertAndSend("/topic/greetings", text);
}
}
destination映射是支持Ant风格的,如"/foo*"
, "/foo/**"
。也支持路径参数"/foo/{id}"
,可以在方法参数中使用@DestinationVariable
注解来引用它
@MessageMapping 注解的方法可以使用下列参数:
* 使用@Payload方法参数用于获取消息中的payload(即消息的内容)
* 使用@Header 方法参数用于获取特定的头部
* 使用@Headers方法参数用于获取所有的头部存放到一个map中
* java.security.Principal 方法参数用于获取在websocket握手阶段使用的用户信息