转载请在页首注明作者与出处

http://www.cnblogs.com/zhuxiaojie/p/6238826.html

一:本文使用范围

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

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

二:Spring boot使用websocket

2.1:依赖包

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

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

javax

javaee-api

7.0

provided

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

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

org.springframework.boot

spring-boot-starter-websocket

1.4.0.RELEASE

2.2:配置websocket

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

@Configurationpublic classWebSocketConfig {

@BeanpublicServerEndpointExporter serverEndpointExporter() {return newServerEndpointExporter();

}

}

2.3:websocket的java代码

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

@ServerEndpoint(value = "/websocket")

@Componentpublic classMyWebSocket {//与某个客户端的连接会话,需要通过它来给客户端发送数据

privateSession session;/*** 连接成功*/@OnOpenpublic voidonOpen(Session session) {this.session =session;

}/*** 连接关闭调用的方法*/@OnClosepublic voidonClose() {

}/*** 收到消息

*

*@parammessage*/@OnMessagepublic voidonMessage(String message, Session session) {

System.out.println("来自浏览器的消息:" +message);//群发消息

for(MyWebSocket item : webSocketSet) {try{

item.sendMessage(message);

}catch(IOException e) {

e.printStackTrace();

}

}

}/*** 发生错误时调用*/@OnErrorpublic voidonError(Session session, Throwable error) {

System.out.println("发生错误");

error.printStackTrace();

}public void sendMessage(String message) throwsIOException {this.session.getBasicRemote().sendText(message);//同步//this.session.getAsyncRemote().sendText(message);//异步

}

}

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

2.4:浏览器端的代码

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

varwebsocket= null;//判断当前浏览器是否支持WebSocket

if('WebSocket' inwindow){

websocket= newWebSocket("ws://localhost:9999/websocket");

}else{

alert('不支持websocket')

}//连接发生错误

websocket.οnerrοr= function(){

};//连接成功

websocket.onopen= function(event){

}//接收到消息

websocket.onmessage= function(event){varmsg=event.data;

alert("收到消息:" +msg);

}//连接关闭

websocket.onclose= function(){

}//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。

window.οnbefοreunlοad= function(){

websocket.close();

}//发送消息

functionsend(message){

websocket.send(message);

}

如此就连接成功了。

三:获取HttpSession,源码分析

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

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

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

packagejavax.websocket.server;importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;importjavax.websocket.Decoder;importjavax.websocket.Encoder;

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.TYPE)public @interfaceServerEndpoint {/*** URI or URI-template that the annotated class should be mapped to.

*@returnThe 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的子类,我们写一个类去继承它。

importjavax.servlet.http.HttpSession;importjavax.websocket.HandshakeResponse;importjavax.websocket.server.HandshakeRequest;importjavax.websocket.server.ServerEndpointConfig;importjavax.websocket.server.ServerEndpointConfig.Configurator;

public class HttpSessionConfigurator extendsConfigurator {

@Overridepublic voidmodifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {

//怎么搞?}

}

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

packagejavax.websocket.server;importjava.net.URI;importjava.security.Principal;importjava.util.List;importjava.util.Map;/*** Represents the HTTP request that asked to be upgraded to WebSocket.*/

public interfaceHandshakeRequest {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>getHeaders();

Principal getUserPrincipal();

URI getRequestURI();booleanisUserInRole(String role);/*** Get the HTTP Session object associated with this request. Object is used

* to avoid a direct dependency on the Servlet API.

*@returnThe javax.servlet.http.HttpSession object associated with this

* request, if any.*/Object getHttpSession();

Map>getParameterMap();

String getQueryString();

}

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

/*** Get the HTTP Session object associated with this request. Object is used

* to avoid a direct dependency on the Servlet API.

*@returnThe javax.servlet.http.HttpSession object associated with this

* request, if any.*/Object getHttpSession();

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

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

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

importjavax.servlet.http.HttpSession;importjavax.websocket.HandshakeResponse;importjavax.websocket.server.HandshakeRequest;importjavax.websocket.server.ServerEndpointConfig;importjavax.websocket.server.ServerEndpointConfig.Configurator;/*** 从websocket中获取用户session

*

**/

public class HttpSessionConfigurator extendsConfigurator {

@Overridepublic voidmodifyHandshake(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的源码:

packagejavax.websocket;importjava.util.List;importjava.util.Map;public interfaceEndpointConfig {

List>getEncoders();

List>getDecoders();

MapgetUserProperties();

}

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

Map getUserProperties();

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

所以就有了上面的

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

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

3.2:设置HttpSession的类

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

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

importjavax.servlet.ServletRequestEvent;importjavax.servlet.ServletRequestListener;importjavax.servlet.http.HttpServletRequest;importorg.springframework.stereotype.Component;

@Componentpublic class RequestListener implementsServletRequestListener {public voidrequestInitialized(ServletRequestEvent sre) {//将所有request请求都携带上httpSession

((HttpServletRequest) sre.getServletRequest()).getSession();

}publicRequestListener() {

}public voidrequestDestroyed(ServletRequestEvent arg0) {

}

}

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

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

这是使用@Bean注解的方式

@Autowird

privateRequestListener requestListener;

@Beanpublic ServletListenerRegistrationBeanservletListenerRegistrationBean() {

ServletListenerRegistrationBean servletListenerRegistrationBean = new ServletListenerRegistrationBean<>();

servletListenerRegistrationBean.setListener(requestListener);returnservletListenerRegistrationBean;

}

或者也可以使用@WebListener注解

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

3.3:在websocket中获取用户的session

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

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

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

@OnOpenpublic voidonOpen(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。

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

  1. Java并发编程笔记之 CountDownLatch闭锁的源码分析

    转 自: Java并发编程笔记之 CountDownLatch闭锁的源码分析 ​ JUC 中倒数计数器 CountDownLatch 的使用与原理分析,当需要等待多个线程执行完毕后在做一件事情时候 C ...

  2. Java Review - SimpleDateFormat线程不安全原因的源码分析及解决办法

    文章目录 概述 复现问题 源码分析 How to Fix ? 每次使用时new一个SimpleDateFormat的实例 加锁 使用ThreadLocal 换API - JodaTime or JDK ...

  3. 【Java】NIO中Selector的select方法源码分析

    该篇博客的有些内容和在之前介绍过了,在这里再次涉及到的就不详细说了,如果有不理解请看[Java]NIO中Channel的注册源码分析, [Java]NIO中Selector的创建源码分析 Select ...

  4. Java的三种代理模式【附源码分析】

    Java的三种代理模式&完整源码分析 代理模式分为两种,静态代理和动态代理,动态代理包括JDK动态代理和Cglib动态代理. 静态代理 静态代理在使用时,需要定义接口或者父类,被代理对象与代理 ...

  5. Java中ConcurrentHashMap底层实现原理(JDK1.8)源码分析2

    https://blog.csdn.net/programmer_at/article/details/79715177 https://blog.csdn.net/qq_41737716/categ ...

  6. 【Java入门提高篇】Day26 Java容器类详解(八)HashSet源码分析

    前面花了好几篇的篇幅把HashMap里里外外说了个遍,大家可能对于源码分析篇已经讳莫如深了.别慌别慌,这一篇来说说集合框架里最偷懒的一个家伙--HashSet,为什么说它是最偷懒的呢,先留个悬念,看完 ...

  7. Java集合(五)LinkedList底层扩容源码分析

    LinkedList的全面说明: (1)LinkedList底层实现了双向链表和双端队列特点 (2)可以添加任意元素(元素可以重复),包括null. (3)线程不安全,没有实现同步 LinkedLis ...

  8. Java面试绕不开的问题: Java中HashMap底层实现原理(JDK1.8)源码分析

    这几天学习了HashMap的底层实现,但是发现好几个版本的,代码不一,而且看了Android包的HashMap和JDK中的HashMap的也不是一样,原来他们没有指定JDK版本,很多文章都是旧版本JD ...

  9. 我的世界java刷怪数量_我的世界Minecraft源码分析(1):刷怪逻辑

    这个系列通过对我的世界Minecraft源码进行拆分讲解,让大家可以清除的了解一款游戏是怎么一步步被实现出来的,下面就介绍Minecraft源码第一篇内容,关于刷怪逻辑. 生成循环 生物大致划分为四种 ...

  10. java log 配置,java日志系统--log4j配置解析过程,源码分析

    log4j 可以看成是非常类似jdk logger 结构 ,有个logger 与logManger 都是在logManger的静态块中初始化类,加载配置文件 Logger.getLogger(Test ...

最新文章

  1. Openstack 实战讲解之-----06-计算节点配置
  2. photoshop中把图片颜色变成透明
  3. Android studio官网资料
  4. Linux 编程--三种常用的定时器
  5. Winform中怎样设置ContextMenuStrip右键菜单的选项ToolStripMenuItem添加照片
  6. Spring Boot返回前端Long型丢失精度
  7. Linux kernel中常见的宏整理
  8. php修罗XiunoBBS轻论坛程序源码开源版
  9. Node.js web应用模块之Supervisor
  10. 个人博客网站的设计与实现_基于BIS的网站建设的设计与实现
  11. C++自增自减运算符简单记录
  12. 乐高机器人纲要_乐高机器人校本课程纲要
  13. 冬瓜哥送你元宵大礼盒啦!!
  14. iscsi 远程连接磁盘
  15. SVN系列——使用教程
  16. 读书可以改变命运,知识可以创造奇迹,这放在任何时代都不会过时
  17. VHDL语言设计8421码加法器(使用quartus)
  18. Android进阶之路 - 版本升级、更新
  19. Prometheus时序数据库-磁盘中的存储结构
  20. Chisel bootcamp 安装 - Centos7.9

热门文章

  1. PyCharm如何配置Qt5开发环境
  2. Hadoop平台搭建与数据分析实验报告
  3. signature=8fe0c6ffeeec1d2ea9caea1e3bd24d0a,[求助]高手来一个解密回题。
  4. [读书笔记]《荒原狼》
  5. 【技术认证题库】SCCA理论aDesk-1考试【初级】
  6. 微信小程序实现微信登录
  7. leetCode100题及答案(转载侵权删)
  8. 电商的基本流程及基础知识点(一)
  9. Ffmpeg 支持的所有格式列表
  10. 「流云行走,代码穿梭:Wails 携手 ChatGPT 打造 MOOC 下载器」