SpringMVC整合DWR3.0 与实例
从网上找了好多资料,都不完整,花了好长时间才弄好,现在整理一下,给朋友们分享一下,也希望能提出问题,完善项目。
1、 首先要下载DWR3.0JAR包(这个是必须的,不多解释哈)
DWR3.0下载地址http://download.csdn.net/detail/qfq1990/7866807
2、 配置项目web.xml文件
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
3、 配置springMVC-servlet.xml文件
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"xmlns:p="http://www.springframework.org/schema/p"
xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dwr="http://www.directwebremoting.org/schema/spring-dwr"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.directwebremoting.org/schema/spring-dwr
http://www.directwebremoting.org/schema/spring-dwr-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
<beanclass="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<propertyname="mediaTypes">
<map>
<entrykey="json" value="application/json"></entry>
<entrykey="xml" value="text/xml"></entry>
<entrykey="htm" value="text/html"></entry>
</map>
</property>
</bean>
<context:component-scanbase-package="com.pfxt.controller" />
<mvc:annotation-driven />
<mvc:resourcesmapping="/resources/**" location="/WEB-INF/resources/"/>
<!-- 对模型视图名称的解析,在请求时模型视图名称添加前后缀 -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<propertyname="viewClass"
value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix"value="/WEB-INF/jsp/" />
<property name="suffix"value=".jsp" />
</bean>
<!-- DWR配置 -->
<bean id="simpleUrlHandlerMapping"class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!-- 要求dwr在spring容器中检查拥有@RemoteProxy和 @RemoteMethod注解的类。注意它不会去检查Spring容器之外的类。 -->
<dwr:annotation-config id="dwr"/>
<!-- 要求DWR将util.js和engine.js映射到dwrController-->
<dwr:url-mapping />
<!-- 定义dwr -->
<dwr:controller id="dwrController"debug="true">
<dwr:config-param name="allowScriptTagRemoting"
value="true" />
<dwr:config-param name="crossDomainSessionSecurity"
value="false" />
<dwr:config-param name="classes"
value="java.lang.Object"/>
<dwr:config-param name="activeReverseAjaxEnabled"
value="true" />
<!-- script session 的超时设置 默认值:1800000(30分钟) -->
<dwr:config-param name="scriptSessionTimeout"
value="3600000" />
<dwr:config-param name="initApplicationScopeCreatorsAtStartup"
value="true" />
<dwr:config-paramname="org.directwebremoting.extend.ScriptSessionManager"
value="com.pfxt.util.DWRScriptSessionManager"/>
</dwr:controller>
<!-- DWR配置 -->
注:这样在服务器启动时即会绑定 ScriptSessionListener, ScriptSession在创建时会自动添加到我们维护的Map中(后面会有解释)
以下是对SercriptSession的优化及维护
服务器的推送功能,但是根据 ScriptSession的生命周期我们可以得出以下几点的问题:
(1)ScriptSession不会与HttpSession同时创建
当我们访问一个页面的时候,如果是第一次访问,就会创建一个新的HttpSession,之后再访问的时候,就会保持当前的Session,即使是刷新,也能保持当前的HttpSession。
但是,ScriptSession不同,第一次访问,会创建一个ScriptSession,但是,如果你刷新,就会创建一个新的ScriptSession.
(2)如何得到ScriptSession
在DWR中,我们可以通过WebContextFactory.get()来取得一个WebContext对象,进而通过WebContext的getScriptSession()取得ScriptSession对象。
但是要注意,在我们自定义的Servlet中,我们也可以通过WebContextFactory.get()来取得一个WebContext,但是这种方法却不能取得ScriptSession对象。因为,此WebContext对象其实不是通过DWR的上下文环境得到的,所以,就根本没有创建ScriptSession对象。
假设这种方式也能得到ScriptSession的话,那么我们实现“推”也就可以不局限在DWR的上下文环境中了,那么其灵活性就会大很多了。所以,这就是我们不能在Servlet中实现推的原因。
(3)关于刷新就创建一个新的ScriptSession问题
在我们需要推送的页面中,如果你刷新一下,那么就提交一个Http的request,此时,如果是第一次,那么就会创建一个httpSession对象,同时,请求由DwrServlet来处理后,就会创建一个ScriptSession.这个ScriptSession会和你的request请求的URI绑定放在一个由ScriptSessionManager维护的Map里面(这里面其实是一个URI对应的Set,在Set里面放置的是URI绑定的所有ScriptSession)。
当你刷新的时候,同样的一个HttpSession,却会创建一个新的ScriptSession,然后绑定到对应的URI上。
(4)向所有的页面访问者推送
当我们想向所有的页面访问者推送的时候,我们只需要,取得所有的页面访问者,就可以“推”了。
如何取得所有的页面访问者?
1. //得到所有ScriptSession
2. Collection<ScriptSession> sessions = Browser.getTargetSessions();
DWR2.x可以通过
1. Collection pages = webContext.getScriptSessionsByPage("/yourPage.jsp");
通过此方法,就可以实现调用客户端的javascript函数,实现对客户端的操作。
(5)在上面的推送中产生的问题
上面的方法已经可以实现向所有的访问者推送。但是问题是,在客户端,如果用户刷新一次或多次,那么,Collection里面可能就保存了很多的无用的ScriptSession,所以不仅仅会影响性能问题,更重要的是,可能就不能实现你想要的功能。
2.如何管理有效的ScriptSession
由于上面的问题,我们就需要自己管理ScriptSession。其实,有效地HttpSession,就是那个和当前的HttpSession匹配的ScriptSession。
所以,我们就可以自己维护一个Map,在这个Map里面,我们定义key就是HttpSession的Id,其值就是ScriptSession对象。
在每一次页面载入的时候,都去注册此ScriptSession,那么就会把新的ScriptSession绑定到httpSession上面了。
在DWR3.0中推出了ScriptSessionListener用来监听ScriptSession的创建及销毁事件。我们可以使用该监听器来维护我们自己的Map。
1.新建一个类实现 ScriptSessionListener接口
1. package sugar.dwr;
2.
3. import java.util.Collection;
4. import java.util.HashMap;
5. import java.util.Map;
6.
7. import javax.servlet.http.HttpSession;
8.
9. import org.directwebremoting.ScriptSession;
10. import org.directwebremoting.WebContext;
11. import org.directwebremoting.WebContextFactory;
12. import org.directwebremoting.event.ScriptSessionEvent;
13. import org.directwebremoting.event.ScriptSessionListener;
14.
15. public class DWRScriptSessionListener implements ScriptSessionListener {
16.
17. //维护一个Map key为session的Id, value为ScriptSession对象
18. public static final Map<String, ScriptSession> scriptSessionMap = new HashMap<String, ScriptSession>();
19.
20. /**
21. * ScriptSession创建事件
22. */
23. public void sessionCreated(ScriptSessionEvent event) {
24. WebContext webContext = WebContextFactory. get();
25. HttpSession session = webContext.getSession();
26. ScriptSession scriptSession = event.getSession();
27. scriptSessionMap.put(session.getId(), scriptSession); //添加scriptSession
28. System. out.println( "session: " + session.getId() + " scriptSession: " + scriptSession.getId() + "is created!");
29. }
30.
31. /**
32. * ScriptSession销毁事件
33. */
34. public void sessionDestroyed(ScriptSessionEvent event) {
35. WebContext webContext = WebContextFactory. get();
36. HttpSession session = webContext.getSession();
37. ScriptSession scriptSession = scriptSessionMap.remove(session.getId()); //移除scriptSession
38. System. out.println( "session: " + session.getId() + " scriptSession: " + scriptSession.getId() + "is destroyed!");
39. }
40.
41. /**
42. * 获取所有ScriptSession
43. */
44. public static Collection<ScriptSession> getScriptSessions(){
45. return scriptSessionMap.values();
46. }
47. }
2.新建一个类继承 DefaultScriptSessionManager,用来绑定 DWRScriptSessionListener
1. package sugar.dwr;
2.
3. import org.directwebremoting.impl.DefaultScriptSessionManager;
4.
5. public class DWRScriptSessionManager extends DefaultScriptSessionManager {
6. public DWRScriptSessionManager(){
7. //绑定一个ScriptSession增加销毁事件的监听器
8. this.addScriptSessionListener( new DWRScriptSessionListener());
9. System. out.println( "bind DWRScriptSessionListener");
10. }
11. }
3.将 DWRScriptSessionManager 配置在 springMVC-servlet.xml中上面已经提到过
这样在服务器启动时即会绑定 ScriptSessionListener, ScriptSession在创建时会自动添加到我们维护的Map中
4.通过以下方法获取所有的 ScriptSession
1. //得到所有ScriptSession
2. Collection<ScriptSession> sessions = DWRScriptSessionListener.getScriptSessions();
3.使用 ScriptSessionFilter过滤
如果我们不想要给所有的客户端 推送消息,只想给特定的客户端推送,那么我们可以使用 ScriptSessionFilter来实现。在filter中去判定session中的Attribute值是不是我们给定的。
1.使用以下方法推送消息
//执行推送
Browser.withAllSessionsFiltered(filter, run); //注意这里调用了有filter功能的方法
2.推送这个地方自己写了一个Util 工具类 SendMessageAutoUtil
import java.util.Collection;
import java.util.List;
import org.directwebremoting.Browser;
import org.directwebremoting.ScriptBuffer;
import org.directwebremoting.ScriptSession;
import org.directwebremoting.ScriptSessionFilter;
import com.pfxt.pojo.Pf_stuxx;
public class SendMessageAutoUtil {
//消息推送 指定某个客户端的页面显示
public static voidsendMessageAuto(String userid,String message,String showScriptMethod) {
final String yhId = userid ;//页面标志
final String autoMessage = message;//返回的数据
final String autoShowScriptMethod =showScriptMethod;//返回页面要调用的JS方法
//执行推送
Browser.withAllSessionsFiltered(newScriptSessionFilter(){
public boolean match(ScriptSessionscriptSession) {
String yhid =(String)scriptSession.getAttribute("yhId");
if(yhid!=null){
return yhId.equals(yhid);
}else{
return false;
}
}
}, new Runnable(){
private ScriptBuffer script = newScriptBuffer();
public void run() {
//设置要调用的 js及参数
script.appendCall(autoShowScriptMethod, autoMessage);
//得到过滤之后的ScriptSession
Collection<ScriptSession> sessions= Browser.getTargetSessions();
//遍历每一个ScriptSession
for (ScriptSession scriptSession: sessions){
scriptSession.addScript(script);
}
}
});//注意这里调用了有filter功能的方法
}
//消息推送 指定某个客户端的页面显示 此方法是把要推送的ID放到集合
public static voidsendMessageListAuto(List targetYhidList,Pf_stuxx stuxx,String showScriptMethod){
final List targetIdList =targetYhidList ;
final Pf_stuxx autoMessage = stuxx;
final String autoShowScriptMethod =showScriptMethod;
//执行推送
Browser. withAllSessionsFiltered(newScriptSessionFilter() {
public boolean match(ScriptSessionscriptSession) {
String yhid =(String)scriptSession.getAttribute("yhId");
if(yhid!=null &&targetIdList.contains(yhid)){
//return yhid.equals(yhId);
targetIdList.remove(yhid);//如果找到了,说明将被推送,所以不用再处理,剩下的都是要被处理的
return true;
}else{
return false;
}
}
}, new Runnable(){
private ScriptBuffer script = newScriptBuffer();
public void run() {
//设置要调用的 js及参数
script.appendCall(autoShowScriptMethod,autoMessage);
//得到过滤之后的ScriptSession
Collection<ScriptSession>sessions = Browser.getTargetSessions();
//遍历每一个ScriptSession
for (ScriptSession scriptSession: sessions){
scriptSession.addScript(script);
}
}
});//注意这里调用了有filter功能的方法
}
}
3.在打开jsp页面时需要在 ScriptSession 中注入设定的attribute,MessagePush中的方法
新建一个Controller
import java.util.List;
import javax.annotation.Resource;
importjavax.servlet.ServletException;
importjavax.servlet.http.HttpSession;
importorg.directwebremoting.ScriptSession;
importorg.directwebremoting.WebContextFactory;
importorg.directwebremoting.annotations.RemoteMethod;
importorg.directwebremoting.annotations.RemoteProxy;
importorg.springframework.stereotype.Controller;
importorg.springframework.ui.Model;
importorg.springframework.web.bind.annotation.RequestMapping;
importorg.springframework.web.bind.annotation.ResponseBody;
import com.pfxt.pojo.Pf_xmjc;
import com.pfxt.pojo.Pf_yhxx;
importcom.pfxt.service.YhxxService;
importcom.pfxt.util.DwrScriptSessionManagerUtil;
@Controller
@RemoteProxy(name="directController")
public class DirectController {
@Resource(name = "yhxxService")
private YhxxService yhxxService;
@RemoteMethod
public void onPageLoad(final String yhId) {
ScriptSession scriptSession =WebContextFactory.get().getScriptSession();
scriptSession.setAttribute("yhId", yhId);
System. out.println("创建连接+"+yhId);
}
}
4.在jsp中调用该方法
<script type="text/javascript"src="<%=path%>/resources/js/jquery-1.9.1.min.js"></script>
<script type="text/javascript"src="<%=path %>/dwr/engine.js"></script>
<script type="text/javascript"src="<%=path %>/dwr/util.js"></script>
<script type="text/javascript" src="<%=path %>/dwr/interface/directController.js"></script>
<script type= "text/javascript">
$(document).ready(function(){
//这个方法用来启动该页面的ReverseAjax功能
dwr.engine.setActiveReverseAjax(true);
//设置在页面关闭时,通知服务端销毁会话
dwr.engine.setNotifyServerOnPageUnload(true);
//设置DWR调用服务出错时,不打印(alert)调试信息
dwr.engine.setErrorHandler(function() {
//
});
onPageLoad();
})
function onPageLoad(){
directController.onPageLoad("568839130");
}
function showMessage(autoMessage){
//
}
</script >
这样我们可以给不同客户端的jsp中导入不同的tag值,过滤推送的客户端.
到此为止spingMVC整合DWR3.0结束,有些地方是转载其他大神的,在大神的基础上做了一些修改和总结,现在分享给大家,共同学习成长。