资源描述
如何开始用DWR
有两种方法开始DWR,简单的方式是下载WAR文件然后看看。但是这不能帮你知道如何轻松的把DWR整合到你的web应用中,所以还是推荐你按照下面的3个步骤做:
1. 安装DWR的Jar包
。把它放到你的webapp的WEB-INF/lib目录下。那里可能已经有很多其他的jar文件了。
2. 编辑配置文件
需要把下面的代码加到文件中。<servlet>那局部需要和其他的<servlet>在一起,<servlet-mapping>局部也一样。
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<display-name>DWR Servlet</display-name>
<servlet-class></servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
在WEB-INF目录下的web.xml旁边创立一个dwr.xml文件。可以从最简单的配置开始:
<!DOCTYPE dwr PUBLIC
"-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN"
" :// getahead.ltd.uk/dwr/dwr10.dtd">
<dwr>
<allow>
<create creator="new" javascript="JDate">
<param name="class" value="java.util.Date"/>
</create>
<create creator="new" javascript="Demo">
<param name="class" value="your.java.Bean"/>
</create>
</allow>
</dwr>
DWR配置文件定义了那些DWR会创立提供远程调用的Javascript类。在上面的例子中我们定义了两个类来提供远程调用,并为其提供的Javascript类的名字。
在上面我们使用了new创立器,它会调用没有参数的构造函数来创立实例,但是所有JavaBean必须有这一构造函数。还要注意DWR有一些限制:
· 不要出现Javascript保存关键字;和保存关键字同名的函数指定被排除。多数Javascript的关键字和Java是相同的。所以你不可能有一个方法叫做"try()"。但是该死"delete()"对与Javascript有着特殊意义,而对Java那么不是。
· Javascript方法重载是不支持的,所以尽量不要再Java中使用。
3. 访问下面的URL
://localhost:8080/[YOUR-WEBAPP]/dwr/
你可以看见一个页面,里面有第二步中的类。接着往里点,你会看到所有可以调用的方法列表。这个页面是动态生成用来测试的例子。
自己动手试一下!
怎么在你的web应用中使用
在文档中有很多例子演示如何动态更改页面中的文字、更新列表、操作表单,还有直接更改table中的内容。每一个都有如何实现的介绍。
另一种方式是看刚刚的页面中提供的代码:
到 ://localhost:8080/\[YOUR-WEBAPP\]/dwr/ 页面,点击你的类。查看源码,找到执行方法的那几行,把那些文字粘贴到你的HTML或JSP中。
要包括下面这些能产生神奇效果的Javascript文件的链接。
<script src='/[YOUR-WEBAPP]/dwr/interface/[YOUR-SCRIPT].js'></script>
<script src='/[YOUR-WEBAPP]/dwr/engine.js'></script>
你也可以把其中/[YOUR-WEBAPP]/替换成你的web页面的相对路径。
DWR根据dwr.xml生成和Java代码类似的Javascript代码。
相对而言Java同步调用,创立与Java代码匹配的Ajax远程调用接口的最大挑战来至与实现Ajax的异步调用特性。
DWR通过引入回调函数来解决这个问题,当结果被返回时,DWR会调用这个函数。
有两种推荐的方式来使用DWR实现远程方法调用。可以通过把回调函数放在参数列表里,也可以把回调函数放到元数据对象里。
当然也可以把回调函数做为第一个参数,但是不建议使用这种方法。因为这种方法在处理自动处理 对象时(查看"Alternative Method")上会有问题。这个方法主要是为向下兼容而存在的。
简单的回调函数
假设你有一个这样的Java方法:
public class Remote {
public String getData(int index) { ... }
}
我们可以在Javascript中这样使用:
<script type="text/javascript"
src="[WEBAPP]/dwr/interface/Remote.js"> </script>
<script type="text/javascript"
src="[WEBAPP]/dwr/engine.js"> </script>
...
function handleGetData(str) {
alert(str);
}
Remote.getData(42, handleGetData);
42是Java方法getData()的一个参数。
此外你也可以使用这种减缩格式:
Remote.getData(42, function(str) { alert(str); });
调用元数据对象(Meta-Data)
另外一种语法时使用"调用元数据对象"来指定回调函数和其他的选项。上面的例子可以写成这样:
Remote.getData(42, {
callback:function(str) { alert(str); }
});
这种方法有很多优点:易于阅读,更重要的指定额外的调用选项。
超时和错误处理
在回调函数的元数据中你可以指定超时和错误的处理方式。例如:
Remote.getData(42, {
callback:function(str) { alert(str); },
timeout:5000,
errorHandler:function(message) { alert("Oops: " + message); }
});
查找回调函数
有些情况下我们很难区分各种回调选项(记住,Javascript是不支持函数重载的)。例如:
Remote.method({ timeout:3 }, { errorHandler:somefunc });
这两个参数之一是bean的参数,另一个是元数据对象,但是我们不能清楚的告诉DWR哪个是哪个。为了可以跨浏览器,我们假定null == undefined。 所以当前的情况,规那么是:
· 如果第一个或最后一个是一个函数,那么它就是回调函数,没有元数据对象,并且其他参数都是Java的方法参数。
· 另外,如果最后一个参数是一个对象,这个对象中有一个callback成员,并且它是个函数,那么这个对象就是元数据对象,其他的都是Java方法参数。
· 另外,如果第一个参数是 null ,我们就假设没有回调函数,并且其他的都是Java方法参数。尽管如此,我们会检查最后一个参数是不是null,如果是就发出警告。
· 最后如果最后一个参数是null,那么就没有callback函数。
· 另外,发出错误信号是个糟糕的请求格式。
创造一个与Java对象匹配的Javascript对象
假设你有这样的Java方法:
public class Remote {
public void setPerson(Person p) {
this.person = p;
}
}
Person对象的结构是这样的:
public Person {
private String name;
private int age;
private Date[] appointments;
// getters and setters ...
}
那么你可以在Javascript中这样写:
var p = {
name:"Fred Bloggs",
age:42,
appointments:[ new Date(), new Date("1 Jan 2021") ]
};
Remote.setPerson(p);
在Javascript没有出现的字段,在Java中就不会被设置。
因为setter都是返回'void',我们就不需要使用callback函数了。如果你想要一个返回void的效劳端方法的完整版,你也可以加上callback函数。很明显DWR不会向它传递任何参数。
TransformerFactoryConfigurationError
这个问题的现象是在启动有DWR的Web应用时出现如下stack trace:
root cause
javax.xml.transform.TransformerFactoryConfigurationError:
Provider org.apache.xalan.processor.TransformerFactoryImpl not found
javax.xml.transform.TransformerFactory.newInstance(Unknown Source)
这个问题和DWR没有什么关系,那是因为Tomcat没有配置好。比拟简单的解决方法是下载Xalan替换掉$TOMCAT-HOME/common/lib目录下的xalan.jar文件。DWR2.0能更好的处理这个问题,但是本质的问题还是因为DWR的XML序列化需要有XSLT解析器的支持。
如果你用JDK5还是有这个问题的话,你可以增加以下VM参数来使Tomcat正常工作。
-Djavax.xml.transform.TransformerFactory=
com.sun.org.
XML解析错误
在刚开始用DWR的时候经常遇到的一个错误就是XML解析错误。其实这和DWR没有多大关系,主要是因为Tomcat里面自带的Xerces的问题,要不是该有的时候没有,要不是不该有的时候有了。
· JDK 1.3自身没有XML解析器,所以你需要xercesImpl.jar和xml-apis.jar.
· JDK 和 JDK 1.4.1 虽然有了XML解析器,但是有很多bug,所以你还是需要把xercesImpl.jar放到tomcat\common\endorsed目录下。
· JDK 和JDK 5自带的XML解析器工作的很好,你就不需要再加其他的了。
另外要提的一点是,不同版本的Tomcat需要的XML解析器不一样。所以要注意检查它和JDK的版本兼容性。
用BEA Weblogic的Classpath问题
Weblogic 8.1(有可能其他版本同样)可能找不到DWR的类。
这大多出现在dwr.jar放在APP-INF目录下(APP_INF/lib)的情况。在这种情况下DWR依然可以工作,例如debug页面可以看见,但是DWR找不到你的类。
解决方法是把dwr.jar放到WEB-INF/lib目录下。
没有cookies的情况下用DWR
当不能用cookies时,servlet标准通过URL重写来支持 Session。DWR 2.x通过它生成的URL来支持这项功能。但是DWR 1.x没有这个功能。你可以通过以下方法让DWR 1.x 也支持cookies:
· 从dwr.jar中提取engine.js,保存到你的文件系统中,就像jsp文件一样.
· 修改"DWREngine._sendData = function(batch)" 方法, 参加一行:
statsInfo += ";jsessionid=" + <%="'"+session.getId()+"'"%>
这样就可以让DWR 1.x支持url重写了。DWR 2+默认支持。
传递额外的数据到callback函数
通常我们需要传递额外的数据到callback函数,但是因为所有的回调函数都只有一个参数(远程方法的返回结果),这就需要一些小技巧了。
解决方案就是使用Javascript的闭包特性。
例如,你的回调函数原本需要像这个样子:
function callbackFunc(dataFromServer, dataFromBrowser) {
// 用dataFromServer和dataFromBrowser做些事情......
}
那么你可以像这个组织你的函数:
var dataFromBrowser = ...;
// 定义一个闭包函数来存储dataFromBrowser的引用,并调用dataFromServer
var callbackProxy = function(dataFromServer) {
callbackFunc(dataFromServer, dataFromBrowser);
};
var callMetaData = { callback:callbackProxy };
Remote.method(params, callMetaData);
(调用元数据在脚本介绍中有解释)
换句话说,现在你作为callback函数传递过来的不是一个真正的callback,他只是一个做为代理的闭包,用来传递客户端的数据。
你可以用更简介的形式:
var dataFromBrowser = ...;
Remote.method(params, {
callback:function(dataFromServer) {
callbackFunc(dataFromServer, dataFromBrowser);
}
});
效劳器性能优化
CPU瓶颈:经过严格的测试DWR的性能没什么问题。DWR上性能消耗同web效劳器和网络比起来可以忽略不计。如果你真的需要提升DWR的性能的话,可以把log级别设置ERROR或FATAL,但是主要还是要看你的编码情况。
Network瓶颈: DWR没有管理你的浏览器缓存的功能,所以它会不断的重复读取DWR的javascript文件。这里有一个简单的解决方法,把javascript文件复制到你的web-app中,这样web效劳器就可以更好的利用它了。你也可以考虑把所有的javascript文件合并成一个文件,然后用DOJO的压缩程序处理一个来节省流量。
我们可以做一个补丁,让DWR在web-app启动的时候用时间做为javascript文件的时间戳,但是这个并不十分重要,因为上面的补丁太简单了而且可以压缩合并Javascript文件。
WEB-INF/web.xml 参考手册
在web.xml中最简单的配置就是简单参加DWR的servlet,没有这个配置DWR就不会起作用:
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class></servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
此外还可以参加一些重要和有用的参数。
Logging
DWR可以工作在JDK1.3上,而JDK1.3不支持java.util.logging,但是我们想强迫任何人使用commons-logging或者log4j,所以当没有logging类的时候DWR就使用 Servlet.log()方法。尽管如此,如果DWR发现了commons-logging,就是使用它。
Commons-Logging
几乎每一个人都在使用commons-logging,因为大多数的servlet容器在使用它。所以如果你的web应用中没有明显的参加commons-logging包,它也会默认的配置好。
在这种情况下,logging是由或者log4j配置文件控制的。详细配置查看文档。
Servlet.log()
如果你用 Servlet.log(), 下面的配置控制logging:
<init-param>
<param-name>logLevel</param-name>
<param-value>DEBUG</param-value>
</init-param>
可用的值有:FATAL, ERROR, WARN (默认), INFO 和 DEBUG。
多个dwr.xml文件 和 J2EE平安
一般来说,你只需要一个dwr.xml文件,并且放置在默认的位置:WEB-INF/dwr.xml。 如果那样的话,你可以不用了解下面的配置。
有三个原因使你希望指定不同位置的dwr.xml文件。
· 你希望让dwr.xml文件和它能访问到的资源在一起。在这种情况下你需要一个这样的配置: <param-value>WEB-INF/classes/com/yourco/dwr/dwr.xml</param-value> 。
· 你有大量的远程调用类,希望把他们分成多个文件。在这种情况下你需要重复下面的配置几次,每一个中有不同的 param-name,并且以 'config' 开头。DWR会依次把他们都读进来。
· DWR可以使用Servlet标准的J2EE的URL平安机制来给不同的用户不同的访问权限。你只需要简单的定义多个dwr servlet,并且制定不同的名字,url和访问权限。
如果你希望使用这一功能,那么语法是这样的:
<init-param>
<param-name>config*****</param-name>
<param-value></param-value>
<description>What config file do we use?</description>
</init-param>
在这里config*****意思是param-name要以字符串config开头。这个参数可以根据需要使用屡次,但是不能相同。
一个使用J2EE的平安机制的例子:
<servlet>
<servlet-name>dwr-user-invoker</servlet-name>
<servlet-class></servlet-class>
<init-param>
<param-name>config-user</param-name>
<param-value></param-value>
</init-param>
</servlet>
<servlet>
<servlet-name>dwr-admin-invoker</servlet-name>
<servlet-class></servlet-class>
<init-param>
<param-name>config-admin</param-name>
<param-value></param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dwr-admin-invoker</servlet-name>
<url-pattern>/dwradmin/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>dwr-user-invoker</servlet-name>
<url-pattern>/dwruser/*</url-pattern>
</servlet-mapping>
<security-constraint>
<display-name>dwr-admin</display-name>
<web-resource-collection>
<web-resource-name>dwr-admin-collection</web-resource-name>
<url-pattern>/dwradmin/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
<security-constraint>
<display-name>dwr-user</display-name>
<web-resource-collection>
<web-resource-name>dwr-user-collection</web-resource-name>
<url-pattern>/dwruser/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>user</role-name>
</auth-constraint>
</security-constraint>
使用插件(Plug-in)
DWR里的很多部件都是可插入的,所以可以通过替换掉DWR的默认实现类来改变其功能。你可以在 <init-param> 中的 param-name 中指定你要替换的接口,并在 param-value 中指定自己的接口实现类。
可插入点是:
· uk.ltd.getahead.dwr.AccessControl
· uk.ltd.getahead.dwr.Configuration
· uk.ltd.getahead.dwr.ConverterManager
· uk.ltd.getahead.dwr.CreatorManager
· uk.ltd.getahead.dwr.Processor
· .getahead.dwr.ExecutionContext
这些可插入点默认的实现都在uk.ltd.getahead.dwr.impl中。
使用debug/test模式
你可以通过下面的参数让DWR进入debug/test模式:
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
在debug模式里,DWR会为每一个远程调用类生成一个测试页面。这对于检查DWR是否工作和工作的怎么样是很有用的。这个模式还可以警告你一些存在的问题:javascript保存字问题,或者函数重载问题。
尽管如此,这个模式不应该使用在实际部署环境里面,因为它可以为攻击者提供你的效劳的大量信息。如果你的网站设计的好的话,这些信息不会帮助攻击者窥视你的网站内容,但是还是不要给任何人一个找到你错误的时机好。
DWR就是照上面的样子做的,没有任何保证,所以你的网站的平安是你的责任。请小心。
dwr.xml是DWR的配置文件。默认情况下,应该把它放到WEB-INF目录(web.xml的目录)下。
DTD
DTD文档以及一个用DTDDoc生成的参考手册。
dwr.xml文件的结构如下:
<!DOCTYPE dwr PUBLIC
"-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN"
" :// getahead.ltd.uk/dwr/dwr10.dtd">
<dwr>
<!-- init is only needed if you are extending DWR -->
<init>
<creator id="..." class="..."/>
<converter id="..." class="..."/>
</init>
<!-- without allow, DWR isn't allowed to do anything -->
<allow>
<create creator="..." javascript="..."/>
<convert converter="..." match="..."/>
</allow>
<!-- you may need to tell DWR about method signatures -->
<signatures>
...
</signatures>
</dwr>
术语
这里是一些必须理解的术语 - 参数会被converted,远程Bean会被created。所以如果你有一个叫A的bean,它有一个方法叫A.blah(B) 那么你需要一个A的creator和一个B的converter。
<allow>
allow段落里面定义的试DWR可以创立和转换的类。
Creators
我们要调用的每个类都需要一个<create ...>定义。creator有几种。比拟通用的是new关键字和Spring。更多的信息可以参见[Creaters]文档。
Converters
我们必须保证所有的参数都可以被转换。JDK中的多数类型已经有转换器了,但是你需要给DWR转换你的代码的权利。一般来说JavaBean的参数需要一个<convert ...>定义。
默认情况下,如下类型不需要定义就可以转换:
· 所有的原生类型 boolean,int,double, 等等
· 原生类型的对象类型 Boolean,Integer,等等
· java.lang.String
· java.util.Date 和SQL中的Date
· 以上类型组成的数组
· 以上类型的集合类型 (Lists, Sets, Maps, Iterators, 等)
· 从DOM, XOM, JDOM 和 DOM4J中的DOM对象 (类似 Element 和 Document)
要了解如何转换你的JavaBean或者其他类型的参数请查看Converters文档。
<init>
可选的init局部用来声明创造bean的类和转换bean的类。多数情况下你不需要用到他们。如果你需要定义一个新的Creator [JavaDoc] 和 Converter [JavaDoc] , 那么你就需要在这里定义他们。但是建议你现检查一下DWR是不是已经支持了。
在init局部里有了定义只是告诉DWR这些扩展类的存在,给出了如何使用的信息。这时他们还没有被使用。这中方式很像Java中的import语句。多数类需要在使用前先import一下,但是只有import语句并不说明这个类已经被使用了。每一个creator和converter都用id属性,以便后面使用。
<signatures>
DWR使用反射来找出在转换时应该用那种类型。有时类型信息并不明确,这时你可以在这里写下方法的签名来明确类型。详细信息查看Signatures局部。
可以有多个dwr.xml文件(详细信息见)。每个文件中的定义会被加在一起。DWR用这个功能来加载根底配置文件。我们可以看看标准被配置文件来了解dwr.xml的内容。
转换器
转换器在客户端和效劳器之间转换数据.
下面这些转换器有单独章节介绍
· Array Converter
· Bean and Object Converters
· Collection Converter
· Enum Converter
· DOM Objects
· Hibernate整合
· Servlet Objects ( ServletRequest, Session, etc)
根底的转换器
原生类型,String,像BigDecimal这样的简单对象的转换器已经有了。你不需要在dwr.xml中<allow>局部的<convert>中定义。它们默认支持。
Date转换器
Date转换器负责在Javascript的Date类型与Java中的Date类型(java.util.Date, java.sql.Date, java.sql.Times or java.sql.Timestamp)之间进行转换。同根底的转换器一样,DateConverter默认是支持的。
如果你有一个Javascript的字符串 (例如"01 Jan 2021") ,你想把它转换成Java的Date类型有两个方法:在javascript中用Date.parse()把它解析成Date类型,然后用DWR的DateConverter传递给效劳器;或者把它作为字符串传递给Server,再用Java中的SimpleDateFormat(或者类似的)来解析。
同样,如果你有个Java的Date类型并且希望在HTML使用它。你可以先用SimpleDateFormat把它转换成字符串再使用。也可以直接传Date给Javascript,然后用Javascript格式化。第一种方式简单一些,尽管浪费了你的转换器,而且这样做也会是浏览器上的显示逻辑受到限制。其实后面的方法更好,也有一些工具可以帮你,例如:
· The Javascript Toolbox Date formatter
· Web Developers Notes on Date formatting
其他对象
其实创立自己的转换器也很简单。Converter接口的Javadoc包含了信息。其实这种需要很少出现。在你写自己的Converter之前先看看BeanConverter,它有可能就是你要的。
The Creators – 创造器
dwr.xml文件中的create元素的结构如下:
<allow>
<create creator="..." javascript="..." scope="...">
<param name="..." value="..."/>
<auth method="..." role="..."/>
<exclude method="..."/>
<include method="..."/>
</create>
...
</allow>
这里的多数元素都是可选的 - 你真正必须知道的是指定一个creator和一个javascript名字。
creator属性 是必须的 - 它用来指定使用那种创造器。
默认情况下DWR1.1有8种创造器。它们是:
· new: 用Java的new关键字创造对象。
· none: 它不创立对象,看下面的原因。 (v1.1+)
· scripted: 通过BSF使用脚本语言创立对象,例如BeanShell或Groovy。
· spring: 通过Spring框架访问Bean。
· jsf: 使用JSF的Bean。 (v1.1+)
· struts: 使用Struts的FormBean。 (v1.1+)
· pageflow: 访问Beehive或Weblogic的PageFlow。 (v1.1+)
如果你需要写自己的创造器,你必须在init局部注册它。
javascript属性 用于指定浏览器中这个被创造出来的对象的名字。你不能使用Javascript的关键字。
scope属性 非常类似servlet标准中的scope。 它允许你指定这个bean在什么生命范围。选项有"application", "session", "request" 和"page"。这些值对于Servlet和JSP开发者来说应该相当熟悉了。
scope属性是可选的。默认是"page"。如果要使用"session"需要cookies。当前的DWR不支持ULR重写。
param元素 被用来指定创造器的其他参数,每种构造器各有不同。例如,"new"创造器需要知道要创立的对象类型是什么。每一个创造器的参数在各自的文档中能找到。请查看上面的链接。
include和exclude元素 允许创造器来限制类中方法的访问。一个创造器必须指定include列表或exclude列表之一。如果是include列表那么暗示默认的访问策略是"拒绝";如果是exclude列表那么暗示默认的访问策略是"允许"。
例如要拒绝防范除了setWibble()以外的所有方法,你应该把如下内容添加到dwr.xml中。
<create creator="new" javascript="Fred">
<param name="class" value="com.example.Fred"/>
<include method="setWibble"/>
</create>
对于参加到create元素中的类的所有方法都是默认可见的。
auth元素 允许你指定一个J2EE的角色作为将来的访问控制检查:
<create creator="new" javascript="Fred">
<param name="class" value="com.example.Fred"/>
<auth method="setWibble" role="admin"/>
</create>
'none' 创造器
'none' 创造器不创立任何对象 - 它会假设你不需要创立对象。这有可能是对的,有两个原因。
你可能在使用的scope不是"page"(看上面),并在在前面已经把这个对象创立到这个scope中了,这时你就不需要再创立对象了。
还有一种情况是要调用的方法是静态的,这时也不需要创立对象。DWR会在调用创立器之前先检查一下这个方法是不是静态的。
对于上诉两种情况,你仍然需要class参数,用来告诉DWR它是在操作的对象类型是什么。
使用静态方法
DWR会在调用创立器之前先检查一下这个方法是不是静态的,如果是那么创造器不会被调用。很显然这个逻辑适用于所有
展开阅读全文