一:本文使用范围

此文不仅仅局限于spring boot,普通的spring工程,甚至是servlet工程,都是一样的,只不过配置一些监听器的方法不同而已。

本文经过作者实践,确认完美运行。

二:Spring boot使用websocket

2.1:依赖包

websocket本身是servlet容器所提供的服务,所以需要在web容器中运行,像我们所使用的tomcat,当然,spring boot中已经内嵌了tomcat。

websocket遵循了javaee规范,所以需要引入javaee的包

<dependency><groupId>javax</groupId><artifactId>javaee-api</artifactId><version>7.0</version><scope>provided</scope></dependency>

当然,其实tomcat中已经自带了这个包。

如果是在spring boot中,还需要加入websocket的starter

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId><version>1.4.0.RELEASE</version></dependency>

2.2:配置websocket

如果不是spring boot项目,那就不需要进行这样的配置,因为如果在tomcat中运行的话,tomcat会扫描带有@ServerEndpoint的注解成为websocket,而spring boot项目中需要由这个bean来提供注册管理。

@Configuration
public class WebSocketConfig {@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}}

2.3:websocket的java代码

使用websocket的核心,就是一系列的websocket注解,@ServerEndpoint是注册在类上面开启。

@ServerEndpoint(value = "/websocket")
@Component
public class MyWebSocket {//与某个客户端的连接会话,需要通过它来给客户端发送数据private Session session;/*** 连接成功*/@OnOpenpublic void onOpen(Session session) {this.session = session;}/*** 连接关闭调用的方法*/@OnClosepublic void onClose() {}/*** 收到消息** @param message */@OnMessagepublic void onMessage(String message, Session session) {System.out.println("来自浏览器的消息:" + message);//群发消息for (MyWebSocket item : webSocketSet) {try {item.sendMessage(message);} catch (IOException e) {e.printStackTrace();}}}/*** 发生错误时调用*/@OnErrorpublic void onError(Session session, Throwable error) {System.out.println("发生错误");error.printStackTrace();}public void sendMessage(String message) throws IOException {this.session.getBasicRemote().sendText(message);//同步//this.session.getAsyncRemote().sendText(message);//异步
    }}

其实我也感觉很奇怪,为什么不使用接口来规范。即使是因为@ServerEndpoint注解中其它属性中可以定义出一些额外的参数,但相信也是可以抽象出来的,不过想必javaee这样做,应该是有它的用意吧。

2.4:浏览器端的代码

浏览器端的代码需要浏览器支持websocket,当然,也有socket.js可以支持到ie7,但是这个我没用过。毕竟ie基本上没人用的,市面上的浏览器基本上全部都支持websocket。

<!DOCTYPE HTML>
<html>
<head></head><body></body><script type="text/javascript">var websocket = null;//判断当前浏览器是否支持WebSocketif('WebSocket' in window){websocket = new WebSocket("ws://localhost:9999/websocket");}else{alert('不支持websocket')}//连接发生错误
    websocket.onerror = function(){};//连接成功
    websocket.onopen = function(event){}//接收到消息
    websocket.onmessage = function(event){var msg = event.data;alert("收到消息:" + msg);}//连接关闭
    websocket.onclose = function(){}//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload = function(){websocket.close();}//发送消息function send(message){websocket.send(message);}
</script>
</html>

如此就连接成功了。

三:获取HttpSession,源码分析

获取HttpSession是一个很有必要讨论的问题,因为java后台需要知道当前是哪个用户,用以处理该用户的业务逻辑,或者是对该用户进行授权之类的,但是由于websocket的协议与Http协议是不同的,所以造成了无法直接拿到session。但是问题总是要解决的,不然这个websocket协议所用的场景也就没了。

3.1:获取HttpSession的工具类,源码详细分析

我们先来看一下@ServerEndpoint注解的源码

package javax.websocket.server;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;import javax.websocket.Decoder;
import javax.websocket.Encoder;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ServerEndpoint {/*** URI or URI-template that the annotated class should be mapped to.* @return The URI or URI-template that the annotated class should be mapped*         to.*/String value();String[] subprotocols() default {};Class<? extends Decoder>[] decoders() default {};Class<? extends Encoder>[] encoders() default {};public Class<? extends ServerEndpointConfig.Configurator> configurator()default ServerEndpointConfig.Configurator.class;
}

我们看到最后的一个方法,也就是加粗的方法。可以看到,它要求返回一个ServerEndpointConfig.Configurator的子类,我们写一个类去继承它。

import javax.servlet.http.HttpSession;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;
import javax.websocket.server.ServerEndpointConfig.Configurator;
public class HttpSessionConfigurator extends Configurator {@Overridepublic void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {//怎么搞?}
}

当我们覆盖modifyHandshake方法时,可以看到三个参数,其中后面两个参数让我们感觉有点见过的感觉,我们查看一HandshakeRequest的源码

package javax.websocket.server;import java.net.URI;
import java.security.Principal;
import java.util.List;
import java.util.Map;/*** Represents the HTTP request that asked to be upgraded to WebSocket.*/
public interface HandshakeRequest {static final String SEC_WEBSOCKET_KEY = "Sec-WebSocket-Key";static final String SEC_WEBSOCKET_PROTOCOL = "Sec-WebSocket-Protocol";static final String SEC_WEBSOCKET_VERSION = "Sec-WebSocket-Version";static final String SEC_WEBSOCKET_EXTENSIONS= "Sec-WebSocket-Extensions";Map<String,List<String>> getHeaders();Principal getUserPrincipal();URI getRequestURI();boolean isUserInRole(String role);/*** Get the HTTP Session object associated with this request. Object is used* to avoid a direct dependency on the Servlet API.* @return The javax.servlet.http.HttpSession object associated with this*         request, if any.*/Object getHttpSession();Map<String, List<String>> getParameterMap();String getQueryString();
}

我们发现它是一个接口,接口中规范了这样的一个方法

    /*** Get the HTTP Session object associated with this request. Object is used* to avoid a direct dependency on the Servlet API.* @return The javax.servlet.http.HttpSession object associated with this*         request, if any.*/Object getHttpSession();

上面有相应的注释,说明可以从Servlet API中获取到相应的HttpSession。

当我们发现这个方法的时候,其实已经松了一口气了。

那么我们就可以补全未完成的代码

import javax.servlet.http.HttpSession;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;
import javax.websocket.server.ServerEndpointConfig.Configurator;/*** 从websocket中获取用户session***/
public class HttpSessionConfigurator extends Configurator {@Overridepublic void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {HttpSession httpSession = (HttpSession) request.getHttpSession();sec.getUserProperties().put(HttpSession.class.getName(), httpSession);}
}

其实通过上面的源码分析,你们应该知道了HttpSession的获取。但是下面又多了一行代码

 sec.getUserProperties().put(HttpSession.class.getName(), httpSession);

这行代码又是什么意思呢?

我们看一下ServerEnpointConfig的声明

public interface ServerEndpointConfig extends EndpointConfig

我们发现这个接口继承了EndpointConfig的接口,好,我们看一下EndpointConfig的源码:

package javax.websocket;import java.util.List;
import java.util.Map;public interface EndpointConfig {List<Class<? extends Encoder>> getEncoders();List<Class<? extends Decoder>> getDecoders();Map<String,Object> getUserProperties();
}

我们发现了这样的一个方法定义

Map<String,Object> getUserProperties();

可以看到,它是一个map,从方法名也可以理解到,它是用户的一些属性的存储,那既然它提供了get方法,那么也就意味着我们可以拿到这个map,并且对这里面的值进行操作,

所以就有了上面的

sec.getUserProperties().put(HttpSession.class.getName(), httpSession);

那么到此,获取HttpSession的源码分析,就完成了。

3.2:设置HttpSession的类

我们之前有说过,由于HTTP协议与websocket协议的不同,导致没法直接从websocket中获取协议,然后在3.1中我们已经写了获取HttpSession的代码,但是如果真的放出去执行,那么会报空指值异常,因为这个HttpSession并没有设置进去。

好,这一步,我们来设置HttpSession。这时候我们需要写一个监听器。

import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.http.HttpServletRequest;import org.springframework.stereotype.Component;@Component
public class RequestListener implements ServletRequestListener {public void requestInitialized(ServletRequestEvent sre)  {//将所有request请求都携带上httpSession
        ((HttpServletRequest) sre.getServletRequest()).getSession();}public RequestListener() {}public void requestDestroyed(ServletRequestEvent arg0)  {}
}

然后我们需要把这个类注册为监听器,如果是普通的Spring工程,或者是servlet工程,那么要么在web.xml中配置,要么使用@WebListener注解。

因为本文是以Spring boot工程来演示,所以这里只写Spring boot配置Listener的代码,其它的配置方式,请自行百度。

这是使用@Bean注解的方式

@Autowird
private RequestListener requestListener;
  @Beanpublic ServletListenerRegistrationBean<RequestListener> servletListenerRegistrationBean() {ServletListenerRegistrationBean<RequestListener> servletListenerRegistrationBean = new ServletListenerRegistrationBean<>();servletListenerRegistrationBean.setListener(requestListener);return servletListenerRegistrationBean;}

或者也可以使用@WebListener注解

然后使用@ServletComponentScan注解,配置在启动方法上面。

3.3:在websocket中获取用户的session

然后刚才我们通过源码分析,是知道@ServerEndpoint注解中是有一个参数可以配置我们刚才继承的类。好的,我们现在进行配置。

@ServerEndpoint(value = "/websocket" , configurator = HttpSessionConfigurator.class)

接下来就可以在@OnOpen注解中所修饰的方法中,拿到EndpointConfig对象,并且通过这个对象,拿到之前我们设置进去的map

@OnOpenpublic void onOpen(Session session,EndpointConfig config){HttpSession httpSession= (HttpSession) config.getUserProperties().get(HttpSession.class.getName());User user = (User)httpSession.getAttribute(SessionName.USER);if(user != null){this.session = session;this.httpSession = httpSession;}else{//用户未登陆try {session.close();} catch (IOException e) {e.printStackTrace();}}}

这下我们就从java的webscoket中拿到了用户的session。

原文地址:https://www.cnblogs.com/zhuxiaojie/p/6238826.html点击打开链接

java使用websocket,并且获取HttpSession,源码分析相关推荐

  1. java的双亲委派模型-附源码分析

    1.类加载器 1.1 类加载的概念   要了解双亲委派模型,首先我们需要知道java的类加载器.所谓类加载器就是通过一个类的全限定名来获取描述此类的二进制字节流,然后把这个字节流加载到虚拟机中,获取响 ...

  2. java实现多线程的方式并源码分析

    个人资源与分享网站:首页-小草资源分享网站 方式一:继承Thread 定义一个MyThread类,重写run方法 public class MyThred extends Thread{ @Overr ...

  3. Java线程池(3)- JUC Executors 源码分析

    4.JUC Executors 使用&源码分析 未完待续,写作中- 1.JUC里有几种线程池?各自的使用场景? FixedThreadPool public static ExecutorSe ...

  4. Java集合框架之接口Collection源码分析

    本文我们主要学习Java集合框架的根接口Collection,通过本文我们可以进一步了解Collection的属性及提供的方法.在介绍Collection接口之前我们不得不先学习一下Iterable, ...

  5. hisi3516dv300芯片基于hwmon驱动框架的温度获取驱动源码分析

    1.内核hwmon驱动框架 参考博客:<内核hwmon驱动框架详解以及海思芯片温度驱动分析>: 2.驱动实现的效果 /sys/devices/virtual/hwmon/hwmon0 # ...

  6. 【java】java中的线程池 ThreadPoolExecutor源码分析

    文章目录 1.概述 4.源码 4.1 关键属性 4.2 构造函数 4.4 状态控制 4.5 ThreadLocalMap 4.6 execute方法源码分析 4.7 addWorker方法源码分析 4 ...

  7. 3.Java集合-HashSet实现原理及源码分析

    一.HashSet概述: HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持,它不保证set的迭代顺序很久不变.此类允许使用null元素 二.HashSet的实现: 对于Ha ...

  8. Java -- 基于JDK1.8的LinkedList源码分析

    1,上周末我们一起分析了ArrayList的源码并进行了一些总结,因为最近在看Collection这一块的东西,下面的图也是大致的总结了Collection里面重要的接口和类,如果没有意外的话后面基本 ...

  9. Java并发编程笔记之FutureTask源码分析

    FutureTask可用于异步获取执行结果或取消执行任务的场景.通过传入Runnable或者Callable的任务给FutureTask,直接调用其run方法或者放入线程池执行,之后可以在外部通过Fu ...

  10. Java程序员必备!Dagger2源码分析(二(1)

    提起hook概念,相信大家都听说过,然后hook就是将反射和动态代理运用于Android平台源码之上的.可是,反射的应用场景缺非常广泛,注解的内容也同样需要使用反射去获取,所以反射是我们Android ...

最新文章

  1. CV08-数据预处理与数据增强
  2. 大数据分析,在中国,找个身高1米7年入20万的老公,到底有多难?
  3. android alpha不起作用,API 28(P)的Android设计支持库不起作用
  4. php csv,php csv操作类代码
  5. [YTU]_2630 ( E2 驾驭const)
  6. java打印星号_【其它】编写Java程序在屏幕上打印用星号组成的等腰三角形
  7. 也来盘点一些最近的非Transformer工作
  8. 项目的数据存储c语言,C语言项目实战项目8__项目中学生数据的存储与重用.ppt
  9. 基于Native Client的编程框架
  10. 2017年秋招-广联达面试及思考
  11. 软考系统架构师笔记-综合知识重点(二)
  12. C语言实现windows进程遍历
  13. ubuntu 14.04 android jdk,Ubuntu 14.04 安裝 jdk8u20 並配置環境變量 安裝Android Studio
  14. java使用泛型_Java 泛型
  15. 计算机组成原理试题2,计算机组成原理试题2
  16. H264/AVC-基本概念和码流结构
  17. Windows更改鼠标滚轮的滚动速度(系统各个软件中上下翻页的速度)
  18. 大话数据结构学习笔记(8)二叉树
  19. 深圳哈工大计算机系的课表,学在哈深|哈工大(深圳)学子课表大比拼,今天你满课了吗?...
  20. 某农商行用户画像项目——模型构建部分

热门文章

  1. .net人员用Java 之Java EE
  2. System.IO命名空间
  3. 关于ADO.NET的困惑
  4. Android解压boot.img
  5. 高通MSM8953 Android7.1蓝牙接电话流程(App到Adsp)(十六)
  6. 一键杀死最近打开APP
  7. Android OMX介绍(总括)
  8. java之list均分
  9. 工具之AlwaysUp
  10. 计算机基础(八):linux编程规范总结