假定項目中須要新增一個功能,管理員發佈某些信息,這些信息須要推送到全部已經登陸的普通用戶頁面。javascript
簡單起見,複用上一篇博客的項目例子,【DWR系列】-DWR簡介及入門例子。即在原項目上直接新增測試。項目結構圖以下:html
修改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
能夠設置爲隨服務器啓動而加載。瀏覽器
新增被推送頁面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.js
和util.js
,並在頁面加載完後聲明啓用逆向Ajax。session
複用上一篇博客的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
首先啓動項目,而後訪問index.jsp
模擬管理員登陸,再開兩個瀏覽器或者標籤頁,登陸normal.jsp
模擬普通用戶登陸,登陸以下:ide
第一個爲上一個篇博客的js調用Java方法頁面,下面兩個模擬普通用戶登陸,而後在有參無返回值輸入框輸入文本,點擊按鈕發送觀察下面了個頁面,發現內容幾乎當即顯示出來:
逆向Ajax
俗稱服務端推送
,可是實際意義上的服務端推送在現有條件下是實現不了的,能夠設想一下,若服務端能夠主動推送內容到客戶端,那麼當訪問惡意網站的時候,會有可能被推送病毒或者木馬。因此通常所謂的服務器端推送都是經過其它方式來實現的,好比說輪詢或者長鏈接。
DWR的逆向Ajax(Reverse Ajax)有三種模式:
實現簡單的對這三種模式進行比較:
Comet
(幾乎瞬時)>Polling
(可自由設置輪詢時間)>Piggyback
(因其不肯定性)Comet
>Polling
>Piggyback
經過比較能夠發現,高性能等價於高消耗,當系統的主要功能須要用推送來完成且實時性要求高鏈接數不大的狀況下可使用Comet,鏈接數較大且對實時性沒有較高要求(一分鐘或以上)可使用Polling,不建議使用Piggyback。
無論使用哪一種逆向推送都會面臨一個問題,那就是被推送客戶端的選擇問題,大多數狀況消息須要被推送到指定的客戶端或指定角色的一系列客戶端。首先經過下面一個例子進行直觀認識。
依然爲了簡便起見,複用原有項目。在上面進行簡單修改。
既然要選擇不一樣的客戶端進行推送,就要有選擇的依據,Web項目中經常使用的選擇依據就是根據用戶不一樣,進行區分。可是要有用戶就要有登陸模塊,再次經過其它方式進行模擬。
在訪問被推送頁面的時候,將用戶ID經過參數傳遞給JSP,例如:
http://localhost:8080/dwr/normal.jsp?userId=yiwangzhibujian
再將獲取到的userId
放到session
,至此就模擬完登陸過程:
String userId=request.getParameter("userId");
session.setAttribute("userId",userId);
正常系統的登陸操做通常都會講用戶ID放到session中,此處進行簡單模擬。
以前已經在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>
後續將對註釋內容進行詳解。
依然複用HelloWorld
類,實際項目中最好不這麼作:
/** * 當頁面開啓時註冊用戶 */ 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
來完成這個工做。
推送方法咱們複用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); } }
依然和上面的測試同樣,登陸三個頁面,第一個爲推送信息頁面,後兩個爲用戶頁面,注意帶上用戶參數:
而後在第一個頁面的有參有返回值框進行輸入測試,測試結果以下:
測試結果經過,能夠進行精準推送。