DWR高級主題之反向Ajax(DWR3介紹)
----------
我們在前面使用的DWR主要是基于DWR2.X版本的,這里我們針對DWR3進(jìn)行介紹,介紹一些提示或技巧。
1.
ScriptSession生命周期(創(chuàng)建ScriptSession以及讓ScriptSession失效)當(dāng)/dwr/engine.js被包含進(jìn)頁面時(shí)ScriptSessions就創(chuàng)建了。默認(rèn)情況下,ScriptSessions的生命周期由org.directwebremoting.impl.DefaultScriptSessionManager維護(hù)。
如果你調(diào)用下面這個(gè)javascript方法:
- dwr.engine.setNotifyServerOnPageUnload(true);
當(dāng)頁面被卸載(比如強(qiáng)制刷新頁面,卸載再加載)時(shí),將對ScriptSessionManager發(fā)出一個(gè)遠(yuǎn)程的DWR調(diào)用,通知它讓ScriptSession失效。DWR通過這個(gè)默認(rèn)的同步調(diào)用,可以很好地讓ScriptSession失效。關(guān)閉瀏覽器時(shí),此同步調(diào)用可能會(huì)導(dǎo)致延遲。如果不喜歡,你可以傳遞第二個(gè)參數(shù)給dwr.engine.setNotifyServerOnPageUnload:
- dwr.engine.setNotifyServerOnPageUnload(true, true);
第二個(gè)可選參數(shù)告訴DWR調(diào)用異步卸載器。
注意:在DWR2.X中,頁面每刷新一次會(huì)多創(chuàng)建一個(gè)新的ScriptSession,使用上面的方式可以有效解決這個(gè)問題。
由于ScriptSession的創(chuàng)建機(jī)制不同于HttpSession,它會(huì)在每次頁面刷新的時(shí)候都會(huì)重新創(chuàng)建,而銷毀機(jī)制卻是失去連接或者失效之后一定時(shí)間才會(huì)自動(dòng)銷毀,這樣就可能造成服務(wù)端可能就保存了很多的無用的ScriptSession,所以不僅僅會(huì)影響性能問題,更重要的是,可能就不能實(shí)現(xiàn)你想要的功能。
解決方法是在接收消息的頁面,也就是你調(diào)用dwr.engine.setActiveReverseAjax(true);的頁面調(diào)用一個(gè)dwr的方法。
dwr.engine.setNotifyServerOnPageUnload(true);
這個(gè)方法的功能就是在銷毀或刷新頁面時(shí)銷毀當(dāng)前ScriptSession,這樣就保證了服務(wù)端獲取的ScriptSession集合中沒有無效的ScriptSession對象。
2
.非DWR線程(關(guān)于請求信息傳遞到非DWR線程)非DWR線程都沒有提到DWR線程創(chuàng)建他們。正因?yàn)槿绱?
WebContextFactory().get().getScriptSession()在非DWR線程中將返回null。你需要通過DWR線程向非DWR線程傳遞數(shù)據(jù)。
3.
ScriptSessionManager(從一個(gè)非DWR線程獲取ScriptSessionManager)可以使用下面的代碼:
- Container container = ServerContextFactory.get().getContainer();
- ScriptSessionManager manager = container.getBean(ScriptSessionManager.class);
4.
可擴(kuò)展性(應(yīng)用程序線程是可擴(kuò)展的,而請求線程則不是)大多數(shù)反向AJAX實(shí)現(xiàn)需要一個(gè)單獨(dú)的線程將數(shù)據(jù)推給客戶端。為每一個(gè)DWR請求創(chuàng)建一個(gè)線程沒有可擴(kuò)展性。我們建議你使用線程池結(jié)合application范圍內(nèi)的DWR創(chuàng)建器。
5
.Browser API(如何針對特定的ScriptSessions)DWR的Browser API包含幾個(gè)比較有用的方法用來更新瀏覽器。一些Browser方法需要ScriptSessionFilter,并根據(jù)ScriptSession的attribute來過濾并針對指定的ScriptSessions。如何使用ScriptSessionFilter與Browser API來區(qū)分用戶:
5.1
implement ScriptSessionFilter- public class TestScriptSessionFilter implements ScriptSessionFilter{
-
- private String attributeName;
-
- public TestScriptSessionFilter(String attributeName){
- this.attributeName = attributeName;
- }
- public boolean match(ScriptSession session){
- Object check = session.getAttribute(attributeName);
- return (check != null && check.equals(Boolean.TRUE));
- }
- }
5.2
在ScriptSession上設(shè)置一個(gè)屬性-
- ScriptSession scriptSession = WebContextFactory.get().getScriptSession();
- String attributeName = "attr";
- scriptSession.setAttribute(attributeName, true);
注:這必須是從DWR發(fā)起的一個(gè)線程(WebContextFactory需要它)。
5.3 在你的反向AJAX線程中使用ScriptSessionFilter
- ScriptSessionFilter filter = new TestScriptSessionFilter(attributeName);
- Browser.withPageFiltered(page, filter, new Runnable(){
- public void run(){
-
- Util.setValue("divID", "value of div");
- }
- });
或者調(diào)用一個(gè)命名函數(shù):
- ScriptSessionFilter filter = new TestScriptSessionFilter(attributeName);
- Browser.withPageFiltered(page, filter, new Runnable(){
- public void run(){
-
-
- ScriptSessions.addFunctionCall("yourJavaScriptFunctionName", arg1, arg2, etc.);
- }
- });
重要的是要注意幾個(gè)Browser方法需要一個(gè)WebContext,請求必須來自DWR線程。目前這幾個(gè)方法需要它:withCurrentPageFiltered,withCurrentPage和getTargetSessions.所有其它方法都能在非DWR線程里安全調(diào)用。
6. ScriptSession(在ScriptSession中設(shè)置區(qū)分屬性)
區(qū)分用戶在同一頁上最常見的方式之一,是在ScriptSession上設(shè)置屬性與在一個(gè)反向Ajax線程上獲取它們。目前在ScriptSession上有兩個(gè)最好的設(shè)置屬性的方法:
6.1 調(diào)用一個(gè)遠(yuǎn)程的DWR方法
- public void remoteMethod() {
- String value = "someValue";
- ScriptSession scriptSession = WebContextFactory.get().getScriptSession();
- scriptSession.setAttribute("key", value);
- }
6.2
使用ScriptSessions創(chuàng)建和銷毀時(shí),將通知ScriptSessionListener。有關(guān)詳細(xì)信息,請參見ScriptSessionListener。- public void sessionCreated(ScriptSessionEvent ev) {
- HttpSession session = WebContextFactory.get().getSession();
- String userId = (String) session.getAttribute("userId");
- ev.getSession().setAttribute("userId", userId);
- }
一旦ScriptSession已填充屬性,反向Ajax線程可以使用Browser API(推薦)與ScriptSessionFilter來針對指定ScriptSession或從ScriptSession來檢索屬性來區(qū)分用戶。
7.
ScriptSessionListeners(當(dāng)ScriptSession創(chuàng)建或銷毀時(shí),如何使用ScriptSessionListeners做處理)你需要如下代碼:
- Container container = ServerContextFactory.get().getContainer();
- ScriptSessionManager manager = container.getBean(ScriptSessionManager.class);
- ScriptSessionListener listener = new ScriptSessionListener() {
- public void sessionCreated(ScriptSessionEvent ev) {
- HttpSession session = WebContextFactory.get().getSession();
- String userId = (String) session.getAttribute("userId");
- ev.getSession().setAttribute("userId", userId);
- }
- public void sessionDestroyed(ScriptSessionEvent ev) { }
- };
- manager.addScriptSessionListener(listener);
這里要注意的是,必須在DWR已初始化后添加ScriptSessionListeners。通常有兩種方法做到這一點(diǎn):
7.1
在DWR servlet初始化后,擴(kuò)展DWR Servlet并執(zhí)行上述代碼7.2
在一個(gè)Servlet中執(zhí)行上述代碼,并把這個(gè)Servlet的<load-on-startup/>值設(shè)置得要比DWR servlet的<load-on-startup/>值高。