1、前言
页面端通常有需求想要准实时知道后台数据的一个变化情况,比如扫码登录场景,或者跳转到网银支付场景,在旧有的短轮训实现下,通常造成大量的不必要请求和查询,这里基于spring websocket+sockjs来解决该问题。
2、websocket
websocket是html5的一个新特性,目前oracle已经统一java websocket api,只要容器支持JSR356(tomcat7以上支持),且jdk使用的是1.7以上,servlet-api3.1,即可使用websocket api提供服务。
在tomcat7之前也提供了tomcat专有的WebsocketServlet,不过在tomcat7已经deprecated,将在tomcat8中移除。
2.1使用jsr356标准的接口实现websocket
- tomcat提供了websocket调用示例,可参考server code和html code
依赖
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>javax.websocket</groupId> <artifactId>javax.websocket-api</artifactId> <version>1.1</version> <scope>provided</scope> </dependency>
注意websocket的scope需要是provided,在运行时使用tomcat提供的jar运行,不然建立连接时会404。
js实现websocket调用
建立连接和注册事件
var target = "ws://127.0.0.1/websocket/testWsAnnotation"; if ('WebSocket' in window) { ws = new WebSocket(target); } else if ('MozWebSocket' in window) { ws = new MozWebSocket(target); } else { alert('WebSocket is not supported by this browser.'); return; } ws.onopen = function () { log('Info: WebSocket connection opened.'); }; ws.onmessage = function (event) { log('Received: ' + event.data); }; ws.onclose = function () { log('Info: WebSocket connection closed.'); };
关闭连接
if (ws != null) { ws.close(); ws = null; }
发送消息
if (ws != null) { var message = document.getElementById('message').value; log('Sent: ' + message); ws.send(message); } else { alert('WebSocket connection not established, please connect.'); }
需要注意的是,js建立连接处完整的js代码要执行完成退出后才会真正发起建立连接请求,如果在此之前发送消息则会报错如下:
InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable
如果是sockjs则会报错如下
Error: InvalidStateError: The connection has not been established yet
服务端实现
需要增加一个配置类,tomcat启动时会将扫描到的配置注解或者实现Endpoint的类传入该配置类方法,再由该配置类中的方法过滤哪些可做为暴露websocket服务的
public class WebSocketConfig implements ServerApplicationConfig { @Override public Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> scanned) { Set<ServerEndpointConfig> result = new HashSet<ServerEndpointConfig>(); for (Class<? extends Endpoint> ep : scanned) { result.add(ServerEndpointConfig.Builder.create(ep, "/websocket/" + WordUtils.uncapitalize(ep.getSimpleName())).build()); } return result; } @Override public Set<Class<?>> getAnnotatedEndpointClasse