【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的主要功能,更详细的介绍和源码分析会写在后续的博客中。

相关文章
相关标签/搜索