Spring整合WebSocket(三)上

简介: Spring整合WebSocket(三)上

一. Spring 整合 WebSocket

一.一 提升点

相对于上一章节的代码实例,主要有以下几个增强点:


  1. 将WebSocket 与Spring 进行整合,扩展性好。
  2. 聊天室人员的名称由用户自已输入,而不是系统指定。
  3. 增加了在线人员列表展示的功能
  4. 转义字符如 <,>等处理。
  5. 界面和功能优化了很多。


一.二 整合流程讲解

Spring 整合 WebSocket, 也是针对 onopen,onclose,onmessage,onerror 四个事件,进行相应处理。


需要多添加一个拦截器和处理器,并将拦截器和处理器,进行注册,需要一个注册工厂。


一.二.一 拦截器 HandshakeInterceptor

开发者需要自定义拦截器, 如 MyHandshakeInterceptor, 实现 org.springframework.web.socket.server.HandshakeInterceptor 接口。


该HandshakeInterceptor 接口提供了两个方法:

public abstract interface HandshakeInterceptor
{
  public abstract boolean beforeHandshake(ServerHttpRequest paramServerHttpRequest, 
ServerHttpResponse paramServerHttpResponse, WebSocketHandler paramWebSocketHandler, 
Map<String, Object> paramMap) throws Exception;
  public abstract void afterHandshake(ServerHttpRequest paramServerHttpRequest, 
   ServerHttpResponse paramServerHttpResponse, WebSocketHandler paramWebSocketHandler, 
  Exception paramException);
}


beforeHandshake() 方法,是请求连接之前的处理方法。


注意,方法的参数 paramServerHttpRequest 并不是以前的 HttpServletRequest 对象,而是 ServletServerHttpRequest 对象, WebSocket 对其进行了扩展。

参数 paramMap 是集体Map, 放置于 WebSocketSession里面, 通过 WebSocketSession对象的 getAttributes() 方法来获取这个Map.

当请求连接时,需要把对象放置到 paramMap 里面进行保存。


afterHandshake() 方法,是请求连接成功之后的处理方法。


一.二.二 处理器 WebSocketHandler

开发者需要自定义处理器, 如 MyWebSocketHandler, 实现 org.springframework.web.socket.WebSocketHandler 接口。


一.二.二.一 接口方法解释

该接口 WebSocketHandler 提供了五个方法。

public abstract interface WebSocketHandler
{
  public abstract void afterConnectionEstablished(WebSocketSession paramWebSocketSession)
    throws Exception;
  public abstract void handleMessage(WebSocketSession paramWebSocketSession, WebSocketMessage<?> paramWebSocketMessage) 
throws Exception;
  public abstract void handleTransportError(WebSocketSession paramWebSocketSession, Throwable paramThrowable)
    throws Exception;
  public abstract void afterConnectionClosed(WebSocketSession paramWebSocketSession, CloseStatus paramCloseStatus)
    throws Exception;
  public abstract boolean supportsPartialMessages();
}


  1. 方法 afterConnectionEstablished(),是连接之后进行的操作,类似于以前的 onopen 方法。 里面有一个参数 WebSocketSession,表示连接进来的那一个 Session. 可以通过 getAttributes() 方法,获取 HandshakeInterceptor 拦截器放置的 paramMap 集合。


  1. 方法 handleMessage(), 是服务器接收浏览器发送过来的消息进行的操作,类似于以前的 onmessage 方法。 WebSocketSession 对象表示 发送消息的那一个Session, WebSocketMessage 表示发送的消息主体。


  1. 方法 handleTransportError()是出现异常时进行的操作,类似于以前的 onerror 方法。 WebSocketSession 对象表示哪一个Session 出现了错误异常。


  1. 方法 afterConnectionClosed(),是浏览器断开连接或者服务器断开连接的操作,类似于以前的 onclose 方法,WebSocketSession 表示 断开的是哪一个Session


  1. 方法 supportsPartialMessages() 表示是否支持拆分。 当浏览器输入的内容过多时,允不允许将接收到的内容,进行拆分处理。通常不允许拆分, 返回 false 即可。


一.二.二.二 接口方法处理操作

在执行 afterConnectionEstablished()时,需要将该Session的对象,放置到在线用户列表里面, 并且向客户端发送’欢迎Xxx进来’ 类似提示。


在执行 handleMessage()时,需要向客户端发送 ‘XXX 说 输入内容’ 类似提示,通过 paramWebSocketMessage.getPayload().toString() 来获取传递过来的内容,通过 paramWebSocketMessage.getPayloadLength() 来判断传递过来的内容是否为空。


在执行 handleTransportError()时,需要从在线用户列表里面移除该Session 对象,并且向客户端发送 ‘Xxx退出聊天室’ 类似提示


在执行 afterConnectionClosed()时,也需要从在线用户列表里面移除该Session 对象,并且向客户端发送 ‘Xxx有事离开了’ 类似提示。


一.二.二.三 服务器向浏览器发送消息

消息 需要封装在 TextMessage 对象里面, 通过

TextMessage message=new TextMessage(内容主体字符串);


进行实例化。


通过调用 WebSocketSession 对象的 sendMessage(WebSocketMessage<?> message) 方法,进行发送消息。

//发送消息
  webSocketSession.sendMessage(message);


一.二.三 注册工厂 WebSocketConfigurer

开发者需要手动实现 注册工厂,来将 HandshakeInterceptor 拦截器和 WebSocketHandler 处理器注册进来, 让系统框架能够通过前台的url地址找到对应的 拦截器和处理器。 如 WebSocketConfig 类。


需要实现org.springframework.web.socket.config.annotation.WebSocketConfigurer 接口

public interface WebSocketConfigurer {
  //注册进来
  void registerWebSocketHandlers(WebSocketHandlerRegistry registry);
}


如:

registry.addHandler(new MyWebSocketHandler(),"/ws").addInterceptors(new MyHandshakeInterceptor());


其中, “/ws” 是前台传入过来的路径。 当前台传入的路径是 ws时,就执行 MyWebSocketHandler 处理器和 MyHandshakeInterceptor 拦截器。


可配置多个。


如:

registry.addHandler(new MyWebSocketHandler(),"/ws").addInterceptors(new MyHandshakeInterceptor());
registry.addHandler(new MyWebSocketHandler2(),"/ws2").addInterceptors(new MyHandshakeInterceptor2());


表示前台路径是 ws时,去执行MyWebSocketHandler,MyHandshakeInterceptor,


当前台路径是 ws2 时,去执行 MyWebSocketHandler2,MyHandshakeInterceptor2, 可以区分性处理。


不要忘记,该注册工厂 WebSocketConfig 需要添加一个注解, @EnableWebSocket, 来表示该类由WebSocket 进行处理。


二. Spring 整合 WebSocket 的详细步骤

二.一 基本环境搭建 (Maven 管理项目)

二.一.一 pom.xml 依赖管理

<dependencies>
    <!-- 添加webmvc的依赖 -->
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>4.2.4.RELEASE</version>
    </dependency>
    <!-- 添加websocket的依赖,不能忘记 -->
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-websocket</artifactId>
    <version>4.2.4.RELEASE</version>
    </dependency>
    <!-- 添加 message依赖 -->
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-messaging</artifactId>
    <version>4.3.14.RELEASE</version>
    </dependency>
    <!-- tomcat中的 servlet-api和 jsp-api -->
  <dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>jsp-api</artifactId>
  <version>2.0</version>
  <scope>provided</scope>
  </dependency>
  <dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>3.1.0</version>
  <scope>provided</scope>
  </dependency>
  <!-- jstl 与 standard -->
  <dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>jstl</artifactId>
  <version>1.2</version>
  </dependency>
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
    </dependency>
    <!-- 日志 -->
    <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.22</version>
    </dependency>
    <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.25</version>
    </dependency>
    <!-- jackson 依赖,用于处理json -->
    <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.2.3</version>
    </dependency>
    <dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-mapper-asl</artifactId>
    <version>1.9.11</version>
    </dependency>
  </dependencies>
  <!-- 构建信息管理 -->
  <build>
  <plugins>
    <!-- 编译的jdk版本 -->
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.1</version>
    <configuration>
      <source>1.8</source>
      <target>1.8</target>
    </configuration>
    </plugin>
    <plugin>
    <groupId>org.apache.tomcat.maven</groupId>
    <!--tomcat的插件名, tomcat7-maven-plugin, 用的是tomcat7版本 -->
    <artifactId>tomcat7-maven-plugin</artifactId>
    <version>2.2</version>
    <configuration>
      <port>8080</port>  <!--tomcat的端口号 -->
      <path>/chatroom</path> <!--tomcat的项目名 -->
      <uriEncoding>UTF-8</uriEncoding> <!-- 防止get 提交时乱码 -->
    </configuration>
    </plugin>
  </plugins>
  </build>


二.一.二 配置 web.xml 文件

比平常多了一个 defaultHtmlEscape ,防止 XSS 注入。

<!-- UTF-8 编码过滤器 -->
  <filter>
  <filter-name>characterEncodingFilter</filter-name>
  <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
  <init-param>
    <param-name>encoding</param-name>
    <param-value>UTF-8</param-value>
  </init-param>
  <init-param>
    <param-name>forceEncoding</param-name>
    <param-value>true</param-value>
  </init-param>
  </filter>
  <filter-mapping>
  <filter-name>characterEncodingFilter</filter-name>
  <url-pattern>/*</url-pattern>
  </filter-mapping>
  <!-- spring mvc前端控制器 -->
  <servlet>
  <servlet-name>SpringMVC</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:springmvc.xml</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
  <servlet-name>SpringMVC</servlet-name>
  <url-pattern>/</url-pattern>
  </servlet-mapping>
  <!-- 防 XSS -->
  <context-param>
  <param-name>defaultHtmlEscape</param-name>
  <param-value>true</param-value>
  </context-param>


二.一.三 springmvc.xml 配置文件

放置在 src/main/resources 目录下。


采用 json 进行转换, 静态资源在 /static 目录下。

<!-- bean组件扫描 -->    
    <context:component-scan base-package="com.yjl.websocket" />
  <mvc:annotation-driven>
  <mvc:message-converters>
    <bean
    class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
    <property name="supportedMediaTypes">
      <list>
      <value>application/json;charset=UTF-8</value>
      </list>
    </property>
    </bean>
  </mvc:message-converters>
  </mvc:annotation-driven>
  <!-- 静态资源 -->
  <mvc:resources location="/static/" mapping="/static/**" />
  <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  <property name="prefix" value="/WEB-INF/pages/" />
  <property name="suffix" value=".jsp" />
  </bean>


二.二 前端页面处理

image.png


二.二.一 前端 static 静态目录

里面存放的是 bootstrap 框架和 jquery,sockjs 的js 文件

二.二.二 index.jsp 页面

主页,去跳转到 登录页面

<body>
<jsp:forward page="User/toLogin"></jsp:forward>
</body>

二.二.三 登录页面 /pages/login.jsp

<body>
  <div class="col-sm-6 col-sm-offset-3">
  <div class="col-sm-offset-2" style="color:#D33;margin-top:30px;">
    <h2>聊天室登录页面</h2>
  </div>
  <div style="margin-top:40px;">
    <form action="${pageContext.request.contextPath}/User/login" method="post" class="form-horizontal" role="form">
    <div class="form-group">
        <label for="firstPass" class="col-md-2 control-label">昵称:</label>
        <div class="col-md-4">
        <input type="text" class="form-control" id="nickName"
          placeholder="请输入你的昵称" name="nickName" value=""/>
        </div>
    </div>
    <div class="form-group">
      <div class="col-sm-offset-3">
      <input type="submit" value="进入聊天室" class="btn btn-success"/>
      </div>
    </div>
    </form>
  </div>
  </div>
</body>


展示大致效果如下所示:

image.png


二.二.四 主页展示 /pages/main.jsp

<%  
  //项目路径
  String path = request.getContextPath();
  //ip地址+端口+项目路径, 即请求前路径
  String basePath = request.getServerName() + ":" + request.getServerPort() + path + "/";
  //协议+basePath
  String baseUrlPath = request.getScheme() + "://" + basePath;
%>
<body>
  <div class="container">
  <div class="row col-sm-offset-4" style="color:#D33;margin-top:30px;padding-left: 60px;">
    <div class="col-sm-7" style="font-size:26px;">欢迎进入 '两个蝴蝶飞' 聊天室</div>
    <div class="col-sm-4 col-sm-offset-1">
    <p>当前登录用户:${sessionScope.loginUser!=null?sessionScope.loginUser.nickName:"请登录" }&nbsp;&nbsp;&nbsp;<button id="exitBtn" class="btn btn-default">退出或重新登录</button></p>
    </div>
  </div>
  <div class="row" style="margin-top:30px;">
    <div class="col-sm-3">
    <div>在线人员列表(<span id="onlineNum">0</span>)人</div>
    <ul id="online" class="list-unstyled">
    </ul>
    </div>
    <div class="col-sm-9">
    <div class="showText" id="up">
      <ul id="contentUl" class="list-unstyled">
                 </ul>
    </div>
    <div class="inputText hr">
      <div class="form-group">
      <textarea class="form-control" id="msg" name="msg" placeholder="请输入你想发送的消息" style="min-width: 50%;width:90%;"></textarea>
      </div>
      <div class="form-group col-sm-offset-9" style="margin-top:30px;">
      <input type="button" value="发送消息" id="sendBtn" name="sendBtn" class="btn btn-success"/>
      </div>
    </div>
    </div>
  </div>
  </div>
</body>


展示大致效果如下所示:

image.png

相关文章
|
6月前
|
设计模式 Java Spring
【Spring源码】WebSocket做推送动作的底层实例是谁
我们都知道WebSocket可以主动推送消息给用户,那做推送动作的底层实例究竟是谁?我们先整体看下整个模块的组织机构。可以看到handleMessage方法定义了每个消息格式采用不同的消息处理方法,而这些方法该类并**没有实现**,而是留给了子类去实现。
【Spring源码】WebSocket做推送动作的底层实例是谁
|
4月前
|
前端开发 网络协议 JavaScript
在Spring Boot中实现基于WebSocket的实时通信
在Spring Boot中实现基于WebSocket的实时通信
|
26天前
|
开发框架 前端开发 网络协议
Spring Boot结合Netty和WebSocket,实现后台向前端实时推送信息
【10月更文挑战第18天】 在现代互联网应用中,实时通信变得越来越重要。WebSocket作为一种在单个TCP连接上进行全双工通信的协议,为客户端和服务器之间的实时数据传输提供了一种高效的解决方案。Netty作为一个高性能、事件驱动的NIO框架,它基于Java NIO实现了异步和事件驱动的网络应用程序。Spring Boot是一个基于Spring框架的微服务开发框架,它提供了许多开箱即用的功能和简化配置的机制。本文将详细介绍如何使用Spring Boot集成Netty和WebSocket,实现后台向前端推送信息的功能。
251 1
|
30天前
|
前端开发 Java C++
RSocket vs WebSocket:Spring Boot 3.3 中的两大实时通信利器
本文介绍了在 Spring Boot 3.3 中使用 RSocket 和 WebSocket 实现实时通信的方法。RSocket 是一种高效的网络通信协议,支持多种通信模式,适用于微服务和流式数据传输。WebSocket 则是一种标准协议,支持全双工通信,适合实时数据更新场景。文章通过一个完整的示例,展示了如何配置项目、实现前后端交互和消息传递,并提供了详细的代码示例。通过这些技术,可以大幅提升系统的响应速度和处理效率。
|
2月前
|
JavaScript 前端开发 Java
【颠覆传统】Spring框架如何用WebSocket技术重塑实时通信格局?揭秘背后的故事与技术细节!
【9月更文挑战第4天】随着Web应用对实时交互需求的增长,传统的HTTP模型已无法满足现代应用的要求,特别是在需要持续、双向通信的场景下。WebSocket协议由此诞生,提供全双工通信渠道,使服务器与客户端能实时互发消息。作为Java开发中最受欢迎的框架之一,Spring通过其WebSocket模块支持这一协议,简化了WebSocket在Spring应用中的集成。
55 0
|
3月前
|
JavaScript 前端开发 网络协议
WebSocket在Java Spring Boot+Vue框架中实现消息推送功能
在现代Web应用中,实时消息提醒是一项非常重要的功能,能够极大地提升用户体验。WebSocket作为一种在单个TCP连接上进行全双工通信的协议,为实现实时消息提醒提供了高效且低延迟的解决方案。本文将详细介绍如何在Java Spring Boot后端和Vue前端框架中利用WebSocket实现消息提醒功能。
158 0
|
5月前
|
前端开发 JavaScript 安全
集成WebSocket在Spring Boot中可以用于实现实时的双向通信
集成WebSocket在Spring Boot中可以用于实现实时的双向通信
86 4
|
4月前
|
监控 网络协议 Java
如何在Spring Boot中使用WebSocket
如何在Spring Boot中使用WebSocket
|
4月前
|
监控 前端开发 网络协议
如何使用Spring Boot实现WebSocket通信
如何使用Spring Boot实现WebSocket通信
|
6月前
|
网络协议 前端开发 Java
Websocket (帧格式, 握手过程, Spring 中使用 WebScoket 协议)
Websocket (帧格式, 握手过程, Spring 中使用 WebScoket 协议)
67 4