博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java使用websocket,并且获取HttpSession,源码分析
阅读量:7119 次
发布时间:2019-06-28

本文共 9009 字,大约阅读时间需要 30 分钟。

一:本文使用范围

此文不仅仅局限于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 class WebSocketConfig {    @Bean    public ServerEndpointExporter serverEndpointExporter() {        return new ServerEndpointExporter();    }}

 

 

 

 

2.3:websocket的java代码

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

 

@ServerEndpoint(value = "/websocket")@Componentpublic class MyWebSocket {    //与某个客户端的连接会话,需要通过它来给客户端发送数据    private Session session;    /**     * 连接成功*/    @OnOpen    public void onOpen(Session session) {        this.session = session;    }    /**     * 连接关闭调用的方法     */    @OnClose    public void onClose() {    }    /**     * 收到消息     *     * @param message     */    @OnMessage    public void onMessage(String message, Session session) {        System.out.println("来自浏览器的消息:" + message);        //群发消息        for (MyWebSocket item : webSocketSet) {            try {                item.sendMessage(message);            } catch (IOException e) {                e.printStackTrace();            }        }    }    /**     * 发生错误时调用     */    @OnError    public 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。

 

如此就连接成功了。

 

 

 

 

 

三:获取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
[] decoders() default {}; Class
[] encoders() default {}; public Class
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 {    @Override    public 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
> 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
> 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 {    @Override    public 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
> getEncoders(); List
> getDecoders(); Map
getUserProperties();}

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

Map
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;@Componentpublic 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;
       @Bean    public ServletListenerRegistrationBean
servletListenerRegistrationBean() { ServletListenerRegistrationBean
servletListenerRegistrationBean = new ServletListenerRegistrationBean<>(); servletListenerRegistrationBean.setListener(requestListener); return servletListenerRegistrationBean; }

或者也可以使用@WebListener注解

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

 

 

 

 

3.3:在websocket中获取用户的session

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

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

 

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

@OnOpen    public 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。

出处

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

转载于:https://www.cnblogs.com/MarchThree/p/6240650.html

你可能感兴趣的文章
4 python 中 关于数值及运算
查看>>
织梦轻量级mvc框架笔记
查看>>
cocos2dx 3.10 网狐土豪金版PC+手机端棋牌平台搭建
查看>>
减小VirtualBox虚拟硬盘文件的大小
查看>>
Hadoop概述
查看>>
Linux系统日志详解
查看>>
Linux笔记(shell特殊符号,sort排序,wc统计,uniq去重,tee,tr,split)
查看>>
11.15PMP试题每日一题
查看>>
华为模拟器如何实现不同Vlan不同网段之间的互通
查看>>
PHP 实现Session入库/存入redis
查看>>
kubernetes集群搭建
查看>>
Spring MVC 入门指南(二):@RequestMapping用法详解
查看>>
motion加树莓派打造实时监控
查看>>
详解MySQL基准测试和sysbench工具
查看>>
使用Spring Session和Redis解决分布式Session跨域共享问题
查看>>
手机锁屏密码忘记了怎么办,清除锁屏的办法
查看>>
BVS烟火识别输油站烟火检测应用
查看>>
使用Apache Ignite构建C++版本的分布式应用
查看>>
数据库基本概念
查看>>
恢复后缀ETH勒索病毒解密方法 恢复sql文件.com].ETH
查看>>