【DWR系列02】-DWR逆向Ajax即服務器推送

1、簡單例子直觀認識

1.1 模擬場景

  假定項目中須要新增一個功能,管理員發佈某些信息,這些信息須要推送到全部已經登陸的普通用戶頁面。javascript

1.2 建立Web項目

  簡單起見,複用上一篇博客的項目例子,【DWR系列】-DWR簡介及入門例子。即在原項目上直接新增測試。項目結構圖以下:html

1.3 修改web.xml

  修改web.xml,使DWR支持逆向Ajax,爲接收DWR請求的servlet簡單的增長一個參數便可:java

<init-param>
    <param-name>activeReverseAjaxEnabled</param-name>
    <param-value>true</param-value>
</init-param>

  最終web.xml以下:web

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
    id="WebApp_ID" version="3.1">
    <display-name>testweb</display-name>
    <servlet>
        <servlet-name>dwr-invoker</servlet-name>
        <!-- 接收js的Ajax請求的servlet -->
        <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
        <!-- 啓用逆向Ajax -->
        <init-param>
            <param-name>activeReverseAjaxEnabled</param-name>
            <param-value>true</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>dwr-invoker</servlet-name>
        <!-- 攔截指定的URL -->
        <url-pattern>/dwr/*</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

  org.directwebremoting.servlet.DwrServlet能夠設置爲隨服務器啓動而加載。瀏覽器

1.4 新增被推送頁面

  新增被推送頁面normal.jsp:服務器

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>

<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<script type='text/javascript' src='dwr/engine.js'></script>
<script type='text/javascript' src='dwr/util.js'></script>
</head>

<body>
    <h2>逆向Ajax頁面,服務器推送</h2>
    <span>推送信息:</span><span id="push"></span>
</body>
<script type="text/javascript">
window.onload = function(){
    dwr.engine.setActiveReverseAjax(true);
}
</script>
</html>

  注意須要引入兩個js文件,engine.jsutil.js,並在頁面加載完後聲明啓用逆向Ajax。session

1.5 服務端推送代碼

  複用上一篇博客的HelloWorld,在有參無返回值方法內,將傳遞給後臺的信息,推送給登陸的頁面,這也符合開始的需求:app

/**
 * 有參無返回值
 */
public void helloYN(final String name){
    System.out.println(new Date().toLocaleString() + " js訪問helloYN方法,name=" + name);
    
    //將接收到的內容推送到全部的瀏覽器
    Browser.withAllSessions(new Runnable(){
        @Override
        public void run(){
            Util.setValue("push",name);
        }
    });
}

  這樣,就能夠用第一篇博客的例子進行測試了。 jsp

1.6 測試

  首先啓動項目,而後訪問index.jsp模擬管理員登陸,再開兩個瀏覽器或者標籤頁,登陸normal.jsp模擬普通用戶登陸,登陸以下:ide

  第一個爲上一個篇博客的js調用Java方法頁面,下面兩個模擬普通用戶登陸,而後在有參無返回值輸入框輸入文本,點擊按鈕發送觀察下面了個頁面,發現內容幾乎當即顯示出來:

2、逆向Ajax簡介

2.1 簡介

  逆向Ajax俗稱服務端推送,可是實際意義上的服務端推送在現有條件下是實現不了的,能夠設想一下,若服務端能夠主動推送內容到客戶端,那麼當訪問惡意網站的時候,會有可能被推送病毒或者木馬。因此通常所謂的服務器端推送都是經過其它方式來實現的,好比說輪詢或者長鏈接。

  DWR的逆向Ajax(Reverse Ajax)有三種模式:

  • Polling:輪詢模式,DWR會以一個固定時間爲週期去服務器獲取數據,這種方式和本身編寫循環執行Ajax同樣。
  • Comet:長鏈接模式,就是服務端持有請求,並不斷的發送數據信息。上面的例子便是Comet模式。
  • Piggyback:捎帶模式,即當有推送需求時,等待下一次Ajax請求一併把數據發送過去。

2.2 各類模式選擇

  實現簡單的對這三種模式進行比較:

  • 響應速度:Comet(幾乎瞬時)>Polling(可自由設置輪詢時間)>Piggyback(因其不肯定性)
  • 對服務器壓力(鏈接數較高時):Comet>Polling>Piggyback

  經過比較能夠發現,高性能等價於高消耗,當系統的主要功能須要用推送來完成且實時性要求高鏈接數不大的狀況下可使用Comet,鏈接數較大且對實時性沒有較高要求(一分鐘或以上)可使用Polling,不建議使用Piggyback。

3、逆向推送進階

  無論使用哪一種逆向推送都會面臨一個問題,那就是被推送客戶端的選擇問題,大多數狀況消息須要被推送到指定的客戶端或指定角色的一系列客戶端。首先經過下面一個例子進行直觀認識。

3.1 建立Web項目

  依然爲了簡便起見,複用原有項目。在上面進行簡單修改。

3.2 設置不一樣屬性值

  既然要選擇不一樣的客戶端進行推送,就要有選擇的依據,Web項目中經常使用的選擇依據就是根據用戶不一樣,進行區分。可是要有用戶就要有登陸模塊,再次經過其它方式進行模擬。

3.2.1 經過URL將用戶傳入

  在訪問被推送頁面的時候,將用戶ID經過參數傳遞給JSP,例如:

http://localhost:8080/dwr/normal.jsp?userId=yiwangzhibujian

3.2.2 設置到session中

  再將獲取到的userId放到session,至此就模擬完登陸過程:

String userId=request.getParameter("userId");
session.setAttribute("userId",userId);

  正常系統的登陸操做通常都會講用戶ID放到session中,此處進行簡單模擬。

3.3 新增js

  以前已經在normal.jsp中開啓了逆向Ajax功能,如今則須要開啓關閉頁面提醒服務器功能:

dwr.engine.setNotifyServerOnPageUnload(true);

  而後將此進行註冊(註冊說法有些不穩當,先這麼理解,後續會對功能進行詳細解釋):

HelloWorld.regist();

  註冊複用了HelloWorld類,在裏面新增了一個方法。

  最終normal.jsp以下:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>

<title>My JSP 'index.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<script type='text/javascript' src='dwr/engine.js'></script>
<script type='text/javascript' src='dwr/util.js'></script>
<script type='text/javascript' src='dwr/interface/HelloWorld.js'></script>
<%
//模擬登陸
//獲取登陸用戶
String userId=request.getParameter("userId");
//將登陸用戶放到session中
session.setAttribute("userId",userId);
%>
</head>

<body>
    <h2>逆向Ajax頁面,服務器推送,當前用戶:${userId }</h2>
    <span>推送信息:</span><span id="push"></span>
</body>
<script type="text/javascript">
window.onload = function(){
    //開啓逆向Ajax功能
    dwr.engine.setActiveReverseAjax(true);
    //開啓關閉頁面提醒服務器功能
    dwr.engine.setNotifyServerOnPageUnload(true);
    //對當前用戶進行註冊
    HelloWorld.regist();
}
</script>
</html>

  後續將對註釋內容進行詳解。

3.4 修改服務端類

  依然複用HelloWorld類,實際項目中最好不這麼作:

3.4.1 新增註冊方法

/**
 * 當頁面開啓時註冊用戶
 */
public void regist(){
    // 獲取當前的scriptSession
    ScriptSession scriptSession=WebContextFactory.get().getScriptSession();
    //獲取HttpSession 並得到其中的userId
    HttpSession session=WebContextFactory.get().getSession();
    String userId=(String) session.getAttribute("userId");
    // 對當前scriptSession的key設置指定的值
    scriptSession.setAttribute("key",userId);
}

  這個方法實際工做是將不一樣的屬性值放置到ScriptSession 中供過濾器使用,實際工做中可使用監聽器ScriptSessionListener 來完成這個工做。

3.4.2 新增推送方法

  推送方法咱們複用HelloWorld的有參有返回值方法,這樣能夠將傳入的參數進行推送,傳入參數限定格式爲,推送用戶 推送內容(忽略校驗,請按格式輸入):

/**
 * 有參有返回值
 */
public String helloYY(final String name){
    //得到傳入的值進行分解,推送用戶 推送內容
    final String[] param=name.split("[ ]{1,}");
    System.out.println(new Date().toLocaleString() + " js訪問helloYY方法,name=" + name);
    //對符合條件的用戶進行推送
    Browser.withAllSessionsFiltered(new ScriptSessionFilter(){

        @Override
        public boolean match(ScriptSession session){
            boolean isYou=param[0].equals(session.getAttribute("key"));
            return isYou;
        }
    },new Runnable(){

        @Override
        public void run(){
            Util.setValue("push",param[1]);
        }
    });
    return "給" + param[0] + "成功推送一條消息";
}

  最終的HelloWorld類內容以下:

package yiwangzhibujian;

import javax.servlet.http.HttpSession;

import org.directwebremoting.*;
import org.directwebremoting.ui.dwr.Util;

import java.util.Date;

/**
 * @author yiwangzhibujian
 */
@SuppressWarnings("deprecation")
public class HelloWorld{

    /**
     * 無參無返回值
     */
    public void helloNN(){
        System.out.println(new Date().toLocaleString() + " js訪問helloNN方法");
    }

    /**
     * 有參無返回值
     */
    public void helloYN(final String name){
        System.out.println(new Date().toLocaleString() + " js訪問helloYN方法,name=" + name);

        // 將接收到的內容推送到全部的瀏覽器
        Browser.withAllSessions(new Runnable(){
            @Override
            public void run(){
                Util.setValue("push",name);
            }
        });

    }

    /**
     * 無參有返回值
     */
    public String helloNY(){
        System.out.println(new Date().toLocaleString() + " js訪問helloNY方法");
        return "Hello World!";
    }

    /**
     * 有參有返回值
     */
    public String helloYY(final String name){
        // 得到傳入的值進行分解,推送用戶 推送內容
        final String[] param=name.split("[ ]{1,}");
        System.out.println(new Date().toLocaleString() + " js訪問helloYY方法,name=" + name);
        // 對符合條件的用戶進行推送
        Browser.withAllSessionsFiltered(new ScriptSessionFilter(){

            @Override
            public boolean match(ScriptSession session){
                boolean isYou=param[0].equals(session.getAttribute("key"));
                return isYou;
            }
        },new Runnable(){

            @Override
            public void run(){
                Util.setValue("push",param[1]);
            }
        });
        return "給" + param[0] + "成功推送一條消息";
    }

    /**
     * 當頁面開啓時註冊用戶
     */
    public void regist(){
        // 獲取當前的scriptSession
        ScriptSession scriptSession=WebContextFactory.get().getScriptSession();
        // 獲取HttpSession 並得到其中的userId
        HttpSession session=WebContextFactory.get().getSession();
        String userId=(String) session.getAttribute("userId");
        // 對當前scriptSession的key設置指定的值
        scriptSession.setAttribute("key",userId);
    }
}

3.5 測試

  依然和上面的測試同樣,登陸三個頁面,第一個爲推送信息頁面,後兩個爲用戶頁面,注意帶上用戶參數:

  而後在第一個頁面的有參有返回值框進行輸入測試,測試結果以下:

  測試結果經過,能夠進行精準推送。

 

  看這篇博客請先看,【DWR系列01】-DWR簡介及入門例子:http://www.cnblogs.com/yiwangzhibujian/p/6145371.html  

  這一篇簡單的介紹了DWR的逆向Ajax即服務端推送,並對服務端精準推送進行了簡單的介紹。至此基本介紹完DWR的主要功能,更詳細的介紹和源碼分析會寫在後續的博客中。

相關文章
相關標籤/搜索