【Spring集成】-Spring配置stomp

1.STOMP的意义:

http协议是无状态协议,即每次请求时都不知道前面发生的什么。而且请求只能由浏览器发起,服务器只能响应该请求,不能主动发送消息给浏览器。这种单向的协议显然在很多场景下是不适用的,比如消息推送,股票实时行情。在websocket之前,我们通常使用Ajax轮询服务器或者使用长轮询,这两种方式都极大消耗了服务端和客户端的资源。而使用websocket,我们只需要借用http协议进行握手,然后保持着一个websocket连接,知道客户端主动断开。相对于另外两种方式,websocket只发送了一次http请求,当服务器有数据时再向浏览器推送数据,减少了带宽的使用以及服务器CPU使用率。

2.Websocket、Http、TCP、Socket之间关系:

HTTP、WebSocket 等应用层协议,都是基于 TCP 协议来传输数据的。 
对于 WebSocket 来说,它必须依赖 HTTP 协议进行一次握手 ,握手成功后,数据就直接从 TCP 通道传输,与 HTTP 无关了。 
Socket并不是一种协议,而是方便我们使用TCP/IP的一种封装,而 WebSocket 则不同,它是一个完整的 应用层协议,包含一套标准的 API 。

3.STOMP协议:

STOMP即Simple (or Streaming) Text Orientated Messaging Protocol,简单(流)文本定向消息协议,它提供了一个可互操作的连接格式,允许STOMP客户端与任意STOMP消息代理(Broker)进行交互。STOMP协议 
许多公司都提供了基于STOMP的服务器与客户端,若spring4开始支持的spring-websocket服务端,基于浏览器的stomp.js客户端 
STOMP定义了客户端和服务器之间以Frame进行同行,Frame的格式为:

COMMAND
header1:value1
header2:value2

Body^@

COMMAND分为CONNECT、SEND、SUBSCRIBE、UNSUBSCRIBE、BEGIN、COMMIT、ABORT、ACK、NACK、DISCONNECT这几种。 
COMMAND之后下一行紧跟着的是头部的键值对,之后加入一条空行,空行之后为body,即传递的消息实体。

传统HTPP请求响应:


websocket请求响应:


4.Spring配置stomp

环境配置:Spring4.3.9+tomcat8+jackson2.8.2

tomacat要8才支持stomp,spring4.0+以后支持stomp,而跨域问题可以通过下面代码解决,但是在spring4.0.9却没有setAllowedOrigins("*")这个函数,所以我选择4.3.9版本registry.addEndpoint("/endpoint").setAllowedOrigins("*").withSockJS();  

而jackson版本过低的话和spring4.3.9不兼容,jackson2.8.2版本没有问题,项目所需类库下载地址:https://download.csdn.net/download/fxkcsdn/10536671

如果没有jackson-core,jackson-databind,jackson-annotations这三个类,则浏览器连接会出现下面情况:


ok,终于要步入正题了: Spring配置stomp

下面开始我们的编程之旅:

第一步:编写实体类:

import java.io.Serializable;
 
public class JinNang implements Serializable{
	private static final long serialVersionUID = 1L; 	
	private String jice;//计策
	private String people;//计策实施者
	
	public JinNang(String jice,String people){
		this.jice=jice;
		this.people=people;
	}
 
	public String getJice() {
		return jice;
	}
 
	public void setJice(String jice) {
		this.jice = jice;
	}
 
	public String getPeople() {
		return people;
	}
 
	public void setPeople(String people) {
		this.people = people;
	}
 
}

People.java

public class People {
	private String name;
	public People(){}
	public People(String name){
		this.name=name;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

这两个实体类要有无参构造函数,待会的消息转换器会使用java映射来将浏览器发送的json数据转换为实体类。

第二步:配置启用代理的web消息功能:

如下的程序展示了如何通过java配置启用基于代理的Web消息功能:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;  
import org.springframework.messaging.simp.config.MessageBrokerRegistry;  
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;  
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;  
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;  
import org.springframework.messaging.simp.SimpMessagingTemplate;  
  
@Configuration  
@ComponentScan
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {  
  
    @Override  
    public void configureMessageBroker(MessageBrokerRegistry config) {  
        config.enableSimpleBroker("/topic","/queue");  
        config.setApplicationDestinationPrefixes("/app");  
    }  
  
    @Override  
    public void registerStompEndpoints(StompEndpointRegistry registry) {  
        registry.addEndpoint("/endpoint").setAllowedOrigins("*").withSockJS();  
    }  
    
  
}

 WebSocketStompConfig 使用了 @EnableWebSocketMessageBroker 注解。这表明这个配置类不仅配置了 WebSocket ,还配置了基于代理的 STOMP 消息。它重载了 registerStompEndpoints() 方法,将 “/endpoint” 注册为 STOMP 端点。这个路径与之前发送和接收消息的目的地路径有所不同。这是一个端点,客户端在订阅或发布消息到目的地路径前,要连接该端点。WebSocketStompConfig 还通过重载 configureMessageBroker() 方法配置了一个简单的消息代理。这个方法是可选的,如果不重载它的话,将会自动配置一个简单的内存消息代理,用它来处理以 “/topic” 为前缀的消息。但是在本例中,我们重载了这个方法,所以消息代理将会处理前缀为 “/topic” 和 “/queue” 的消息。除此之外,发往应用程序的消息将会带有 “/app” 前缀。书上这个截图展示了配置中的消息流:


当消息到达时,目的地的前缀将会决定消息该如何处理。在图 18.2 中,应用程序的目的地以 “/app” 作为前缀,而代理的目的地以 “/topic” 和 “/queue” 作为前缀。以应用程序为目的地的消息将会直接路由到带有 @MessageMapping 注解的控制器方法中。而发送到代理上的消息,其中也包括 @MessageMapping 注解方法的返回值所形成的消息,将会路由到代理上,并最终发送到订阅

这些目的地的客户端.

第三步:构造控制器

import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.messaging.handler.annotation.MessageMapping;  
import org.springframework.messaging.handler.annotation.SendTo;  
import org.springframework.messaging.simp.SimpMessagingTemplate;  
import org.springframework.messaging.simp.annotation.SendToUser;  
import org.springframework.stereotype.Controller;  
  
@Controller  
public class ShuguoController {  
  
//    public SimpMessagingTemplate template;  
//  
//    @Autowired  
//    public ShuguoController(SimpMessagingTemplate template) {  
//        this.template = template;  
//    }  
  
    @MessageMapping("/jinnang")  
    @SendTo("/topic/jinnang")  
    public JinNang getJinNang(People people) throws Exception {
    	System.out.println("people:"+people.getName());
        return new JinNang("火烧赤壁",people.getName());
    }  
}

我们在控制器方法添加@MessageMapping注解,使其处理STOMP消息,他与带有@RequestMapping注解的方法处理HTTP请求的方式非常类似。但是。与@RequestMapping不同的是,@MessageMapping的功能无法通过@EnableWebMvc启用。Spring的Web消息功能基于消息代理构建,因此除了告诉Spring我们想要处理的消息以外,还有其他的内容需要配置。我们必须要配置一个消息代理和其他的一些消息目的地。

但是这个处理器方法与我们之前看到的有一点区别。 getJinnang() 方法没有使用 @RequestMapping 注解,而是使用了 @MessageMapping 注解。这表示 getJinnang()方法能够处理指定目的地上到达的消息。在本例中,这个目的地也就是 “/app/jingnang” ( “/app” 前缀是隐含的,因为我们将其配置为应用的目的地前缀)。因为 getJinnang()方法接收一个 Jinnang参数,所以 Spring 的某一个消息转换器会将 STOMP 消息的负载转换为 JinNang对象。

因为我们现在处理的不是HTTP,所以无法使用Spring的HttpMessageConverter实现将负载转换为JinNang对象。Spring4.0提供了几个消息转换器,作为API的一部分。下图描述了这些消息转换器,在处理STOMP消息的时候可能会用到他们。


假设getJinnang()方法所处理消息的内容类型为“application/json”(这是一个安全的假设,因为JinNang不是byte[]和String),MappingJackson2MessageConverter会负责将JSON消息转换为JinNang对象。就像在HTTP中对应的MappingJackson2HttpMessageConverter一样。MappingJackson2MessageConverter会将其任务委托给底层的Jackson2 JSON处理器。默认情况下,Jackson会使用反射将JSON属性映射为Java对象的属性。

第四步:编写客户端代码:

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <meta name="format-detection" content="telephone=no"/>
    <meta name="format-detection" content="email=no"/>
    <meta http-equiv="Cache-Control" content="no-cache"/>
    <meta http-equiv="Pragma" content="no-cache"/>
    <meta http-equiv="Expires" content="0"/>
    <!--必须导入的三个脚本文件  -->
    <script src="http://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
    <script src="http://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.js"></script>
    <script src="http://cdn.bootcss.com/stomp.js/2.3.3/stomp.js"></script>
</head>
<body class="test">
<script>
	//定义stomp连接服务器的地址:只需要一次HTTP握手就可以进行连接。整个通讯过程是建立在一次连接/状态中,也就避免了HTTP的非状态性
	var url = 'http://localhost:8080/SpringTest/endpoint'  
	var socket = new SockJS(url, undefined, {transports: ['websocket']});  
	//新建stomp客户端
	var stompClient = Stomp.over(socket); 
	//stomp请求与服务器建立连接 connet({},function(),function())第一个参数Map是请求的头信息,第二个参数是请求成功回调函数,第三个函数是请求失败回调函数
	stompClient.connect({}, function(frame) { 
		console.log("connected------------");
		stompClient.subscribe("/topic/jinnang", handleJinNang);
	    sendMessage();
	},
	 function(error){
    	console.log(error.headers.message)
    }
	);  
	function handleJinNang(result){
		var jinnang=JSON.parse(result.body);
		console.log("received:",jinnang);
		document.getElementById("display").value=jinnang.people+"实施"+jinnang.jice;
	}
	function sendMessage(){
		var message=JSON.stringify({"name":"黄盖"})
		stompClient.send("/app/jinnang",{},message);
	}
</script>
<input type="text" id="display"/>
</body>
</html>

send()方法传递的第二个参数是一个头信息的Map,他会包含在STOMP的帧中,不过在这个例子中,我们没有提供任何参数,Map是空的。

   运行截图:


  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值