DWR 3.0 支持純註解配置,結合 Servlet 3.0 ServletContainerInitializer 或 ServletContextListener 實現零 XML 部署。
public class DwrConfig implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext ctx = sce.getServletContext();
// 1. 動態註冊 DwrServlet
DwrServlet dwrServlet = new DwrServlet();
ServletRegistration.Dynamic reg = ctx.addServlet("dwr-invoker", dwrServlet);
reg.setLoadOnStartup(2);
reg.addMapping("/dwr/*");
// 2. 核心初始化參數(必須設置)
reg.setInitParameter("debug", "true");
reg.setInitParameter("pollAndCometEnabled", "true"); // 支持長輪詢/Comet
reg.setInitParameter("activeReverseAjaxEnabled", "true"); // 啟用反向 AJAX
reg.setInitParameter("maxWaitAfterWrite", "1800000"); // 寫入後最大等待(ms)
reg.setInitParameter("disconnectedTime", "60000"); // 斷開連接判定時間(ms)
// 3. 關鍵:classes 參數指定註解類全限定名(多個用逗號分隔)
// 注意:DWR 3.0 不支持包掃描,必須顯式列出
reg.setInitParameter("classes", getAnnotatedClasses());
}
private String getAnnotatedClasses() {
return String.join(",",
"com.g360.bean.reconsole.opcost.AnnualOpCostRequestController",
"com.g360.bean.security.SessionExpiredParam"
);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {}
}
@Component("sessionExpiredParam") // Spring 管理
@RemoteProxy(
creator = SpringCreator.class, // 使用 Spring 創建器
scope = ScriptScope.APPLICATION, // 應用級單例
name = "remote", // JS 端變量名
creatorParams = @Param(name = "beanName", value = "sessionExpiredParam")
)
public class SessionExpiredParam {
private static final Logger LOG = LoggerFactory.getLogger(SessionExpiredParam.class);
public boolean isSessionExpire(String sessionId) {
return ConstantCacheService.getCacheConstant(sessionId) == null;
}
@RemoteMethod // 必須標註才暴露給前端
public void setEnableDwrUpdate(String sessionId) {
ScriptSession scriptSession = WebContextFactory.get().getScriptSession();
String jsessionId = scriptSession.getHttpSessionId();
String dwrSessionId = scriptSession.getId();
LOG.info("綁定推送: jsessionId=[{}], dwrSessionId=[{}]", jsessionId, dwrSessionId);
// 將 DWR Session ID 存入緩存,供後端反向推送使用
ConstantCacheService.putCacheConstant(Constants.PUSH_ID + jsessionId, dwrSessionId);
}
}
<!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 3.0//EN"
"https://getahead.org/dwr/dwr30.dtd">
<dwr>
<allow>
<create creator="spring" javascript="remote" scope="application">
<param name="beanName" value="sessionExpiredParam"/>
</create>
</allow>
</dwr>
@RemoteProxy 類全名。ContextLoaderListener 在 DwrConfig 之前初始化(load-on-startup 順序)。APPLICATION(全局單例)、SESSION(用戶會話)、PAGE(請求級)、SCRIPT_SESSION(DWR 會話)。@RemoteMethod 的公共方法會被暴露,其他方法保持私有。| 錯誤現象 | 可能原因 | 解決方案 |
|---|---|---|
| ClassNotFoundException: SpringCreator | 缺少 dwr-spring.jar | 添加依賴 org.directwebremoting:dwr-spring:3.0.x |
| Bean 'xxx' not found | Spring 上下文未載入該 Bean | 檢查 @Component 掃描路徑、Bean 名稱拼寫 |
| 前端報 "remote is undefined" | classes 參數缺少該類、或 JS 引入路徑錯誤 | 補全 classes、檢查 /dwr/interface/remote.js 路徑 |
| 反向 AJAX 不工作 | activeReverseAjaxEnabled=false 或容器不支持 Comet | 啟用參數、確認 Servlet 容器支援 (Tomcat 7+/Jetty 9+) |
下篇將詳細講解 前端調用後端方法 的完整流程、JS 代理生成機制、參數序列化細節與錯誤處理最佳實踐。