dwr推送spring实现

前端之家收集整理的这篇文章主要介绍了dwr推送spring实现前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

概述

最近项目需要一个主动推送的功能,果断百度、google发现网上主要有两种实现方式:

一种是使用HTML5的webSockect,这个需要Tomcat7以上才支持,而且需要客户端的IE浏览器都支持HTML5。所以被我们果断的放弃的了。

另一种是使用dwr实现,dwr是一个JS与服务端Java类交互的Ajax框架。可以做到后台调用Java类方法的同时前台JS方法执行,前台JS方法执行的同时后台Java类的对应方法被执行。

最后使用了DWR来实现后台前台的数据推送。

基本实现思路:

既然涉及到推送,那么我们肯定要明确推送的发起点以及推送的目标点(每个用户都要有明确ID),并且保证当用户在不同页面跳转时,保证数据都能够推送到前台(会话状态)。在我们的系统中,主要发起点是用户A在前台点击某些操作触发的,推送的目标点是用户B。

Dwr推送的基本思想是将一个后台的Java类映射为一个前台的同名JS类,同时通过JS维持一个长连接,与每个页面产生一个scriptsession,该session保证了长连接的同时也保证了每个链接会话的不同状态。当前台调用java对应的JS类中的某个方法时,dwr调用后台的java类中的同名方法后台java类的某个方法调用的时候,前台的对应JS对象的方法也会被调用,也就实现了向前台推送

实现方式:

1,引入DWR包

2,在web.xml文件中配置:

  1. <listener>
  2. <listener-class>
  3. org.directwebremoting.servlet.DwrListener
  4. </listener-class>
  5. </listener>
  6. <servlet>
  7. <servlet-name>dwr-invoker</servlet-name>
  8. <servlet-class>
  9. org.directwebremoting.servlet.DwrServlet
  10. </servlet-class>
  11. <init-param>
  12. <param-name>crossDomainSessionSecurity</param-name>
  13. <param-value>false</param-value>
  14. </init-param>
  15. <init-param>
  16. <param-name>allowScriptTagRemoting</param-name>
  17. <param-value>true</param-value>
  18. </init-param>
  19. <init-param>
  20. <param-name>classes</param-name>
  21. <param-value>java.lang.Object</param-value>
  22. </init-param>
  23. <init-param>
  24. <param-name>activeReverseAjaxEnabled</param-name>
  25. <param-value>true</param-value>
  26. </init-param>
  27. <init-param>
  28. <param-name>initApplicationScopeCreatorsAtStartup</param-name>
  29. <param-value>true</param-value>
  30. </init-param>
  31. <init-param>
  32. <param-name>maxWaitAfterWrite</param-name>
  33. <param-value>3000</param-value>
  34. </init-param>
  35. <init-param>
  36. <param-name>logLevel</param-name>
  37. <param-value>WARN</param-value>
  38. </init-param>
  39. <init-param>
  40. <param-name>debug</param-name>
  41. <param-value>true</param-value>
  42. </init-param>
  43. </servlet>
  44. <servlet-mapping>
  45. <servlet-name>dwr-invoker</servlet-name>
  46. <url-pattern>/dwr/*</url-pattern>
  47. </servlet-mapping>
以上是在web.xml中的配置,具体配置信息的含义都可以在 http://directwebremoting.org/dwr/documentation/server/configuration/servlet/index.html页面中查看。
3,添加dwr.xml文件
文件默认存放路径在WEB-INF路径下。
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 3.0//EN" "http://getahead.org/dwr/dwr30.dtd">
  3. <!-- 指明暴露给前台JS的后台JAVA类,即将后台的JAVA类转换为前台的JS类,并且JAVA类中的public方法
  4. 都会在JS类中生成同名的JS函数 -->
  5. <dwr>
  6. <allow>
  7. <!-- creator是java类的创建者,有spring(dwr与spring集成的时候使用)、new(单独使用)等
  8. javascript指创建的JS类的名称生成的JS文件名称,这里是MessagePush.js -->
  9. <create creator="spring" javascript="MessagePush">
  10. <!-- name值可以使class,配合creator=new使用.也可以是beanName,配合creator=spring使用
  11. value的值根据不同的情况值不同,当name值为new,那么value的值就是目标Java类的全类名;
  12. 当name的值为beanName时,value的值就是spring实例化出的bean的id -->
  13. <param name="beanName" value="pushUtil"></param>
  14. </create>
  15. <!-- creator是java类的创建者,有spring(dwr与spring集成的时候使用),javascript指创建的
  16. JS类的名称生成的JS文件名称,这里是aclService.js -->
  17. <create creator="spring" javascript="aclService">
  18. <param name="beanName" value="aclManageService"></param>
  19. </create>
  20. <!--
  21. <create creator="new" javascript="MessagePush">
  22. <param name="class" value="com.changan.test.DWRTest"></param>
  23. </create>
  24. -->
  25. </allow>
  26. </dwr>
4,在spring中配置:
  1. <bean id="pushUtil" class="com.changan.common.pushUtils.PushUtil"></bean>
5,在JS中引入如下JS文件,这些JS文件不是实际存在的,而是通过DWR的servlet根据dwr.xml文件配置自动生成的。
  1. <script type="text/javascript" src="<%=basePath%>dwr/engine.js"></script>
  2. <script type="text/javascript" src="<%=basePath%>dwr/util.js"></script>
  3. <script type="text/javascript" src="<%=basePath%>dwr/interface/MessagePush.js"></script>
6,在前台页面调用后台java类的方法
这里的这个方法是在onload方法调用,以保证页面加载成功后就创建一个ScriptSession对象,来保持长连接通话

  1. function onPageLoad(){
  2. var userId = '${users.userId}';//这个userId是用来区分ScriptSession的
  3. //dwr创建的JS对象MessagePush,对应dwr.xml中的定义,这里调用后台方法
  4. MessagePush.onPageLoad(userId);
  5. }
7,定义被Java后台调用前台JS方法

  1. function showMessage(msg){
  2. alert(msg);
  3. }
8,后台Java类:

  • PushUtil类,暴露、转化为JS对象的Java类
  1. /**
  2. *
  3. * @ClassName: PushUtil
  4. * @Description: 推送的主要实现类,由Spring实例化,同时dwr.xml文件中配置.
  5. * @author lixiaodai
  6. * @date 2013-11-2 上午9:50:26
  7. *
  8. */
  9. public class PushUtil {
  10. /**
  11. *
  12. * @Title: onPageLoad
  13. * @Description: 前台页面创建的onload事件会调用这个方法的JS方法,其实和调用这个方法一样
  14. * 该方法会在每次调用时创建一个脚本会话
  15. * @param userId 不同session的回话标识
  16. * @return void 返回类型
  17. * @throws
  18. */
  19. public void onPageLoad(String userId) {
  20. //ScriptSession,DWR中提供的脚本会话对象,这个会话是储存在本地线程中的
  21. ScriptSession scriptSession = WebContextFactory.get().getScriptSession();
  22. //给每个脚本会话赋值一个属性,一般作为脚本会话的区别属性
  23. scriptSession.setAttribute("userId",userId);
  24. //初始化信息
  25. initInfo();
  26. }
  27.  
  28. //初始化方法
  29. private void initInfo() {
  30. //得到当前服务端的dwr容器
  31. Container container = ServerContextFactory.get().getContainer();
  32. //从dwr容器中得到脚本会话管理类
  33. ScriptSessionManager manager = container.getBean(ScriptSessionManager.class);
  34. //从脚本会话管理类中得到目前所有的脚本会话对象
  35. Collection<ScriptSession> sessions = manager.getAllScriptSessions();
  36. //得到当前访问用户的HttpSession
  37. HttpSession httpSession = WebContextFactory.get().getSession();
  38. //判断当前Http会话中是否已经有scriptSessionId属性
  39. //如果有,则说明该HttpSession已经绑定了一个ScriptSession对象
  40. //如果没有,则说明该HttpSession还没有绑定ScriptSession
  41. if(httpSession.getAttribute("scriptSessionId")!=null){
  42. //得到当前HttpSession中存放的scriptSessionId属性
  43. int id = (Integer)httpSession.getAttribute("scriptSessionId");
  44. //遍历所有的ScirptSession对象,尝试将所有ScirptSession的id不是HttpSession中存放的scriptSessionId
  45. //的ScriptSession对象废止,注意:这里是废止不是立刻删除
  46. for(ScriptSession session:sessions){
  47. if(session.hashCode()!=id){
  48. session.invalidate();
  49. }
  50. }
  51. }
  52. // System.out.println("after invalidate sessionId:"+httpSession.getId()+",scriptSessionCount:"+manager.getScriptSessionsByHttpSessionId(httpSession.getId()).size());
  53. //得到会话监听对象
  54. ScriptSessionListener listener = PushListener.getInstance();
  55. //将监听对象添加到ScriptSessionManager管理类上
  56. manager.addScriptSessionListener(listener);
  57. }
  58.  
  59. //这个方法用来推送,也就是当调用这个方法的时候,前台的JS对应函数就会被触发
  60. public static void sendMessageAuto(String userid,String message) {
  61. //由于我们的推送是有目标的,所以需要目标ID以及要推送信息
  62. Browser.withAllSessionsFiltered(new PushFilter(userid),new PushRunable(message));
  63. }
  64. }

  • PushListener类
  1. /**
  2. *
  3. * @ClassName: PushListener
  4. * @Description: 会话监听器类,是一个单例类,这个类主要来当监听到ScriptSession创建,* 那么就分别在新创建的ScriptSession和HttpSession两个不同级别的会话中
  5. * 互相绑定对方的唯一标识
  6. * @author lixiaodai
  7. * @date 2013-11-7 上午9:56:57
  8. *
  9. */
  10. public class PushListener implements ScriptSessionListener{
  11.  
  12. private static PushListener listener;
  13. private PushListener() {
  14.  
  15. }
  16. public static synchronized PushListener getInstance(){
  17. if(listener==null){
  18. listener = new PushListener();
  19. }
  20. return listener;
  21. }
  22. /**
  23. * 会话/长连接创建时调用方法
  24. */
  25. public void sessionCreated(ScriptSessionEvent ev) {
  26. //得到当前的HttpSession类
  27. HttpSession session = WebContextFactory.get().getSession();
  28. //当前登录用户用户ID
  29. String userId = ((Users) session.getAttribute("users")).getUserId() + "";
  30. //向新创建的ScriptSession中添加属性userId,来标识该ScriptSession对应的用户
  31. ev.getSession().setAttribute("userId",userId);
  32. //向HttpSession中设置新生成的ScriptSession对象的ID
  33. session.setAttribute("scriptSessionId",ev.getSession().hashCode());
  34. }
  35. /**
  36. * 会话(长连接)关闭调用方法
  37. */
  38. public void sessionDestroyed(ScriptSessionEvent ev) {
  39. //尝试废止该ScriptSession对象
  40. ev.getSession().invalidate();
  41. }
  42.  
  43. }

  • PushFilter类
  1. /**
  2. *
  3. * @ClassName: PushFilter
  4. * @Description: 这个类用来过滤不同的SessionScript,保证我们要推送的数据能够准确推送到目标的
  5. * ScriptSession中
  6. * @author lixiaodai
  7. * @date 2014-3-21 上午10:31:05
  8. *
  9. */
  10. public class PushFilter implements ScriptSessionFilter {
  11.  
  12. private static PushFilter filter;
  13.  
  14. private String userId;
  15. public PushFilter() {
  16.  
  17. }
  18. public PushFilter(String id){
  19. this.userId = id;
  20. }
  21. /**
  22. * 主要的过滤方法,根据我们的条件来过滤推送到哪个ScriptSession中
  23. */
  24. @Override
  25. public boolean match(ScriptSession session) {
  26. //根据脚本中的userId属性来判断是否是要推送的目标脚本会话
  27. if (session.getAttribute("userId") == null){
  28. return false;
  29. }else{
  30. return (session.getAttribute("userId")).equals(userId);
  31. }
  32. }
  33.  
  34. }

  • PushRunnable
    1. /**
    2. *
    3. * @ClassName: PushRunable
    4. * @Description: 用来实际执行推送的类,这个类是一个线程,同时要设定目标推送的方法以及要推送的信息
    5. * @author lixiaodai
    6. * @date 2014-3-21 上午10:47:19
    7. *
    8. */
    9. public class PushRunable implements Runnable {
    10. private String message;
    11. private ScriptBuffer script = new ScriptBuffer();
    12. public PushRunable(){
    13. }
    14. public PushRunable(String msg){
    15. this.message = msg;
    16. }
    17. /**
    18. * 新启的线程,执行的业务
    19. */
    20. @Override
    21. public void run() {
    22. //要推送到的前台目标的JS方法以及该方法的参数
    23. script.appendCall("showMessage",message);
    24. //这里得到的ScriptSession的集合是通过PushFilter过滤过的
    25. Collection<ScriptSession> sessions = Browser.getTargetSessions();
    26. for (ScriptSession scriptSession : sessions) {
    27. // System.out.println(scriptSession.getAttribute("userId"));
    28. scriptSession.addScript(script);
    29. }
    30. }
    31. }
以上就是简单的推送实现,参照了 http://www.blogjava.net/stevenjohn/archive/2012/07/07/382447.html 并做了一点改进,敬请拍砖,3Q。

猜你在找的Ajax相关文章