最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java使用websocket,并且獲取HttpSession 源碼分析(推薦)

 更新時(shí)間:2017年08月17日 09:52:34   作者:朱小杰  
這篇文章主要介紹了java使用websocket,并且獲取HttpSession,通過使用配置源碼分析了各方面知識(shí)點(diǎn),具體操作步驟大家可查看下文的詳細(xì)講解,感興趣的小伙伴們可以參考一下。

一:本文使用范圍

此文不僅僅局限于spring boot,普通的spring工程,甚至是servlet工程,都是一樣的,只不過配置一些監(jiān)聽器的方法不同而已。

本文經(jīng)過作者實(shí)踐,確認(rèn)完美運(yùn)行。

二:Spring boot使用websocket

2.1:依賴包

websocket本身是servlet容器所提供的服務(wù),所以需要在web容器中運(yùn)行,像我們所使用的tomcat,當(dāng)然,spring boot中已經(jīng)內(nèi)嵌了tomcat。

websocket遵循了javaee規(guī)范,所以需要引入javaee的包

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

當(dāng)然,其實(shí)tomcat中已經(jīng)自帶了這個(gè)包。

如果是在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項(xiàng)目,那就不需要進(jìn)行這樣的配置,因?yàn)槿绻趖omcat中運(yùn)行的話,tomcat會(huì)掃描帶有@ServerEndpoint的注解成為websocket,而spring boot項(xiàng)目中需要由這個(gè)bean來提供注冊(cè)管理。

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

2.3:websocket的java代碼

使用websocket的核心,就是一系列的websocket注解,@ServerEndpoint是注冊(cè)在類上面開啟。

@ServerEndpoint(value = "/websocket")
@Component
public class MyWebSocket {

  //與某個(gè)客戶端的連接會(huì)話,需要通過它來給客戶端發(fā)送數(shù)據(jù)
  private Session session;

  /**
   * 連接成功*/
  @OnOpen
  public void onOpen(Session session) {
    this.session = session;
  }

  /**
   * 連接關(guān)閉調(diào)用的方法
   */
  @OnClose
  public void onClose() {
  }

  /**
   * 收到消息
   *
   * @param message 
  */
  @OnMessage
  public void onMessage(String message, Session session) {
    System.out.println("來自瀏覽器的消息:" + message);

    //群發(fā)消息
    for (MyWebSocket item : webSocketSet) {
      try {
        item.sendMessage(message);
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
  }

  /**
   * 發(fā)生錯(cuò)誤時(shí)調(diào)用
   */
  @OnError
  public void onError(Session session, Throwable error) {
    System.out.println("發(fā)生錯(cuò)誤");
    error.printStackTrace();
  }
  public void sendMessage(String message) throws IOException {
    this.session.getBasicRemote().sendText(message);//同步
    //this.session.getAsyncRemote().sendText(message);//異步
  }
}

其實(shí)我也感覺很奇怪,為什么不使用接口來規(guī)范。即使是因?yàn)锧ServerEndpoint注解中其它屬性中可以定義出一些額外的參數(shù),但相信也是可以抽象出來的,不過想必javaee這樣做,應(yīng)該是有它的用意吧。

2.4:瀏覽器端的代碼

瀏覽器端的代碼需要瀏覽器支持websocket,當(dāng)然,也有socket.js可以支持到ie7,但是這個(gè)我沒用過。畢竟ie基本上沒人用的,市面上的瀏覽器基本上全部都支持websocket。

<!DOCTYPE HTML>
<html>
<head>
</head>
<body>
</body>
<script type="text/javascript">
  var websocket = null;
  //判斷當(dāng)前瀏覽器是否支持WebSocket
  if('WebSocket' in window){
    websocket = new WebSocket("ws://localhost:9999/websocket");
  }
  else{
    alert('不支持websocket')
  }
  //連接發(fā)生錯(cuò)誤
  websocket.onerror = function(){
    
  };
  //連接成功
  websocket.onopen = function(event){  
  }
  //接收到消息
  websocket.onmessage = function(event){
    var msg = event.data;
    alert("收到消息:" + msg);
  }
  //連接關(guān)閉
  websocket.onclose = function(){    
  }
  //監(jiān)聽窗口關(guān)閉事件,當(dāng)窗口關(guān)閉時(shí),主動(dòng)去關(guān)閉websocket連接,防止連接還沒斷開就關(guān)閉窗口,server端會(huì)拋異常。
  window.onbeforeunload = function(){
    websocket.close();
  }
  //發(fā)送消息
  function send(message){
    websocket.send(message);
  }
</script>
</html>

 如此就連接成功了。

三:獲取HttpSession,源碼分析

獲取HttpSession是一個(gè)很有必要討論的問題,因?yàn)閖ava后臺(tái)需要知道當(dāng)前是哪個(gè)用戶,用以處理該用戶的業(yè)務(wù)邏輯,或者是對(duì)該用戶進(jìn)行授權(quán)之類的,但是由于websocket的協(xié)議與Http協(xié)議是不同的,所以造成了無法直接拿到session。但是問題總是要解決的,不然這個(gè)websocket協(xié)議所用的場(chǎng)景也就沒了。

3.1:獲取HttpSession的工具類,源碼詳細(xì)分析

我們先來看一下@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;
}

我們看到最后的一個(gè)方法,也就是加粗的方法??梢钥吹?,它要求返回一個(gè)ServerEndpointConfig.Configurator的子類,我們寫一個(gè)類去繼承它。

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) {
    //怎么搞?
  }
}

當(dāng)我們覆蓋modifyHandshake方法時(shí),可以看到三個(gè)參數(shù),其中后面兩個(gè)參數(shù)讓我們感覺有點(diǎn)見過的感覺,我們查看一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();
}

我們發(fā)現(xiàn)它是一個(gè)接口,接口中規(guī)范了這樣的一個(gè)方法

  /**
   * 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();

上面有相應(yīng)的注釋,說明可以從Servlet API中獲取到相應(yīng)的HttpSession。

當(dāng)我們發(fā)現(xiàn)這個(gè)方法的時(shí)候,其實(shí)已經(jīng)松了一口氣了。

那么我們就可以補(bǔ)全未完成的代碼

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);
  }
}

其實(shí)通過上面的源碼分析,你們應(yīng)該知道了HttpSession的獲取。但是下面又多了一行代碼

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

這行代碼又是什么意思呢?

我們看一下ServerEnpointConfig的聲明

public interface ServerEndpointConfig extends EndpointConfig

我們發(fā)現(xiàn)這個(gè)接口繼承了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();
}

我們發(fā)現(xiàn)了這樣的一個(gè)方法定義

Map<String,Object> getUserProperties();
可以看到,它是一個(gè)map,從方法名也可以理解到,它是用戶的一些屬性的存儲(chǔ),那既然它提供了get方法,那么也就意味著我們可以拿到這個(gè)map,并且對(duì)這里面的值進(jìn)行操作,

所以就有了上面的

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

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

3.2:設(shè)置HttpSession的類

我們之前有說過,由于HTTP協(xié)議與websocket協(xié)議的不同,導(dǎo)致沒法直接從websocket中獲取協(xié)議,然后在3.1中我們已經(jīng)寫了獲取HttpSession的代碼,但是如果真的放出去執(zhí)行,那么會(huì)報(bào)空指值異常,因?yàn)檫@個(gè)HttpSession并沒有設(shè)置進(jìn)去。

好,這一步,我們來設(shè)置HttpSession。這時(shí)候我們需要寫一個(gè)監(jiān)聽器。

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請(qǐng)求都攜帶上httpSession
    ((HttpServletRequest) sre.getServletRequest()).getSession();
  }
  public RequestListener() {
  }
  public void requestDestroyed(ServletRequestEvent arg0) {
  }
}

 然后我們需要把這個(gè)類注冊(cè)為監(jiān)聽器,如果是普通的Spring工程,或者是servlet工程,那么要么在web.xml中配置,要么使用@WebListener注解。

因?yàn)楸疚氖且許pring boot工程來演示,所以這里只寫Spring boot配置Listener的代碼,其它的配置方式,請(qǐng)自行百度。

這是使用@Bean注解的方式

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

或者也可以使用@WebListener注解

然后使用@ServletComponentScan注解,配置在啟動(dòng)方法上面。

3.3:在websocket中獲取用戶的session

然后剛才我們通過源碼分析,是知道@ServerEndpoint注解中是有一個(gè)參數(shù)可以配置我們剛才繼承的類。好的,我們現(xiàn)在進(jìn)行配置。

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

接下來就可以在@OnOpen注解中所修飾的方法中,拿到EndpointConfig對(duì)象,并且通過這個(gè)對(duì)象,拿到之前我們?cè)O(shè)置進(jìn)去的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。

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來一定的幫助~如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持!

相關(guān)文章

  • SpringBoot多數(shù)據(jù)源配置詳細(xì)教程(JdbcTemplate、mybatis)

    SpringBoot多數(shù)據(jù)源配置詳細(xì)教程(JdbcTemplate、mybatis)

    這篇文章主要介紹了SpringBoot多數(shù)據(jù)源配置詳細(xì)教程(JdbcTemplate、mybatis),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • Quarkus集成apollo配置中心

    Quarkus集成apollo配置中心

    這篇文章主要介紹了Quarkus集成apollo配置中心,文中詳細(xì)的講解了Quarkus的config構(gòu)成,以及apollo集成實(shí)現(xiàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2022-02-02
  • Spring中的ApplicationRunner接口的使用詳解

    Spring中的ApplicationRunner接口的使用詳解

    這篇文章主要介紹了Spring中的ApplicationRunner接口的使用詳解,ApplicationRunner使用起來很簡(jiǎn)單,只需要實(shí)現(xiàn)CommandLineRunner或者ApplicationRunner接口,重寫run方法就行,需要的朋友可以參考下
    2023-11-11
  • Java多線程處理文件的示例詳解

    Java多線程處理文件的示例詳解

    在Java編程中,文件處理是一項(xiàng)常見的任務(wù),為了提高文件處理的效率,我們可以使用多線程技術(shù),本文將詳細(xì)介紹如何使用Java多線程來處理文件,需要的可以參考下
    2024-12-12
  • Java中Map與JSON數(shù)據(jù)之間的互相轉(zhuǎn)化

    Java中Map與JSON數(shù)據(jù)之間的互相轉(zhuǎn)化

    我們?cè)陂_發(fā)中難免和JSON打交道,這不小編最近遇到了,需要把一些信息轉(zhuǎn)成JSON字符串,下面這篇文章主要給大家介紹了關(guān)于Java中Map與JSON數(shù)據(jù)之間的互相轉(zhuǎn)化,需要的朋友可以參考下
    2023-04-04
  • springboot熱部署class XX cannot be cast to class XX解決方案

    springboot熱部署class XX cannot be cast&nbs

    在使用DevTools進(jìn)行熱加載時(shí)遇到的`classXXcannotbecasttoclassXX`錯(cuò)誤,以及解決該問題的方法,通過在`resources`目錄下創(chuàng)建`META-INF/spring-devtools.properties`文件,并添加相應(yīng)的配置,可以有效解決此問題,使DevTools熱加載功能得以正常工作
    2025-02-02
  • Java字符串?dāng)?shù)組的創(chuàng)建代碼示例

    Java字符串?dāng)?shù)組的創(chuàng)建代碼示例

    這篇文章主要介紹了Java中字符串?dāng)?shù)組的聲明、初始化、默認(rèn)值、遍歷和常見操作,文中通過代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2025-03-03
  • SpringBoot中實(shí)現(xiàn)Redis緩存預(yù)熱

    SpringBoot中實(shí)現(xiàn)Redis緩存預(yù)熱

    緩存預(yù)熱是一種在系統(tǒng)啟動(dòng)后,但在實(shí)際使用前將數(shù)據(jù)加載到緩存中的技術(shù),本文主要來和大家一起探討如何在Spring Boot應(yīng)用程序中實(shí)現(xiàn)Redis緩存預(yù)熱,以確保系統(tǒng)在處理請(qǐng)求前就已經(jīng)處于最佳狀態(tài),感興趣的可以了解下
    2023-11-11
  • 淺談Java并發(fā)之同步器設(shè)計(jì)

    淺談Java并發(fā)之同步器設(shè)計(jì)

    這篇文章主要介紹Java并發(fā)之同步器設(shè)計(jì),本文以記錄方式并發(fā)編程中同步器設(shè)計(jì)的一些共性特征。并簡(jiǎn)單介紹了Java中的AQS,需要的朋友可以參考一下文章的詳細(xì)內(nèi)容
    2021-10-10
  • futuretask用法及使用場(chǎng)景介紹

    futuretask用法及使用場(chǎng)景介紹

    這篇文章主要介紹了futuretask用法及使用場(chǎng)景介紹,小編覺得挺不錯(cuò)的,這里分享給大家,供大家參考。
    2017-10-10

最新評(píng)論

扶沟县| 岐山县| 佛冈县| 赤城县| 广州市| 广州市| 高淳县| 绥滨县| 金堂县| 金山区| 福鼎市| 横山县| 衡南县| 惠州市| 遵化市| 南开区| 柳林县| 涿鹿县| 专栏| 兰溪市| 婺源县| 张家界市| 镇原县| 宕昌县| 沽源县| 永昌县| 房产| 阿勒泰市| 宾川县| 黄龙县| 惠东县| 屯留县| 苍山县| 新巴尔虎左旗| 南汇区| 陆河县| 遂川县| 望都县| 天峻县| 东台市| 清镇市|