可以通过访问文档来得知如何使用DWR的转换器:
http://directwebremoting.org/dwr/documentation/server/configuration/dwrxml/converters/index.html
下面我们来一步步讲解:
1.转换器(convert)用于负责定义Java类型和js类型之间的对应关系,看看文档的说辞:
Converters are an integral part of DWR and are responsible for marshaling data between the client and the server. You must specify a converter for:
- The Java return type of Java methods you have exposed to DWR. In order to return a JavaScript object that can be used by the browser DWR needs to know how to marshal the Java return type into a JavaScript object.
- The Java parameter type of all parameters of Java methods you have exposed to DWR. In order to properly call the Java method DWR needs to know how to marshal the JavaScript object being passed from the browser into the appropriate Java object.
从上面的文字可以知道,在客户端和服务器,无论是return type和parameter type上,都存在转换的问题,这样就更加凸显转换器的作用。
2.基本转换器(Basic Converters)的使用:
The converters for primitives, strings and simple objects like BigDecimal are all there waiting for you. You don'tneed a <convert ...>
element in the <allow>
section in dwr.xml to use them - They are enabled by default.
The types enabled by default include: boolean, byte, short, int, long, float, double, char, java.lang.Boolean, java.lang.Byte, java.lang.Short, java.lang.Integer, java.lang.Long, java.lang.Float, java.lang.Double, java.lang.Character, java.math.BigInteger, java.math.BigDecimal and java.lang.String
使用基本类型转换器不需要再在<allow>中使用<convert.../>元素来标识,它自己默认启用了。同时,上面的文档也列举了Basic Converters所能支持的类型:boolean、byte、short、int、long、float、double、char、java.lang.Boolean、java.lagne.Byte、java.lang.Short、java.lange.Integer、java.lang.Logn、java.lang.Float、java.lang.Double、java.lang.Character、java.math.BigInteger、java.math.BigDecimal以及java.lang.String。
3.日期转换器(Date Converter)的使用:
The Date converter marshalls between a Javascript Date and a java.util.Date
, java.sql.Date
, java.sql.Times
orjava.sql.Timestamp
. As with the basic converters above, the DateConverter is enabled by default.
If you have a string (e.g. "01 Jan 2010") in Javascript, that you wish to turn into a Java Date then you have 2 options: Parse it in Javascript using Date.parse()
and then pass it to the server using DWR's DateConverter, or pass it as a String and then parse it in Java using a SimpleDateFormat (or something similar).
On the other hand, if you have a date in Java which you wish to display in a usable form in HTML, then you can either transfer it as a string after using a DateFormatter on it, or transfer as a Date and then use Javascript to format it. The first option is probably easier although it does mean messing around with your converted objects, and it's doing something that it strictly view logic further from the browser. The latter option is better really and there are utilities to help you.
DWR提供了Date转换器,Date转换器负责在js的Date类型与Java中的Date类型之间进行转换,DWR默认支持Date转换器。
如果你有一个js的string(比如 2010-01-01),需要转换成一个Java的Date类型,默认提供了两种方法:在js中使用Date.parse()方法,把它解析成js的Date类型,然后由DWR的DateConverter来负责转换成Java的Date对象。或者直接将string传递到服务器端,然后在服务器端使用SimpleDateFormat转换成Date对象。
反过来,如果你有一个Java的Date对象,希望在HTML使用这个值,你可以选择先用SimpleDateFormat把它转换成string在传递给客户端的js使用。或者直接把这个Date对象传给客户端,再用js来格式化他。使用第一种方法可以很简单,但是却是无法再多样化显示了。而第二种就灵活很多,客户端的js代码获得是Date对象,因此可以提供更灵活的显示格式。
4.对象转换器(Bean and Object Converters)的使用
Two converters that are not enabled by default are the Bean and Object converters. They will convert Java Beans and POJOs to and from JavaScript objects. These converters are not enabled by default because DWR makes sure that is has permission before it exposes any of your classes.
DWR默认是关闭Bean converter和Object converter这两种converter的,因此使用这两个转换器必须要显示的通过文档调用才可以的。
我们看看接下来的这段文档:
Beans converted using the BeanConverter need to follow the JavaBeans spec because the converter usesIntrospection and not Reflection. This means things like having properly named getters and setters where there is a single parameter to the setter which matches the return type of the getter. The setter should return void, the getter should not have any parameters and there is no overloading of setters. Mostly this is common sense, but it does trip some people up. If your class does not follow the Java Bean specification then you need the ObjectConverter.
Object converter和Bean converter是很相似的,都是负责完成Java对象和js对象之间的转换。但是Object转换器相对来说强大一点,因为他是使用reflection(反射)来访问Java对象的属性,即Object转换器用于转换普通Java对象(该Java对象并未为每个属性提供setter和getter方法)。而Bean转换器就是通过setter和getter来访问Java对象的属性。
为某个类单独打开一个Bean转换器,可以采用如下代码:
<!-- 对mwj.Mwj类使用Bean转换器--> <convert convertor="bean" match="mwj.Mwj"/> |
好像上面的例子,使用Bean转换器将使用setter和getter方法访问mwj.Mwj对象的各属性。如果使用Bean转换器,那么对应的类必须要符合JavaBean规范,即每一个属性都含有setter和getter方法。
如果同时需要转换某个包下的所有类,可以采用如下格式代码:
<convert convertor="bean" match="mwj.*"/>//这种方法将同时用Bean转换器把mwj包下的所有类进行转换。 |
如果想要把所有的类都进行Bean转换器的转换,可以采用如下的代码:
<convert convertor="bean" match="*"/>//这种方法可以同时用Bean转换器把该应用中的所有类都转换。 |
我们看看文档是如何让我们去操作DWR的:
public class Remoted { }//Remoted类里面的addToFriends方法调用了Person类的参数。根据前面学习的“术语”知道,这个时候Remoted类是要被create的,而Person类是要被convert的。 private String name;//文档的例子没有这些的,我补上去是为了规范化的JavaBean。 private int age;//文档的例子没有这些的,我补上去是为了规范化的JavaBean。 public void setName(String name) { ... } //文档的例子没有这些的,我补上去是为了规范化的JavaBean。 public String getName() { ... } //文档的例子没有这些的,我补上去是为了规范化的JavaBean。 public int getAge() { ... } |
那么我们可以在前端的js代码中写到如下的内容:
var p = { name:"Fred", age:21 };//用JSON格式创建了一个js对象。明显这是一个Person的对象。 |
如果我们希望不转换一个类中的某个属性,我们可以通过使用类似与create的白名单或者黑名单的方式来操作,那么就可以这样用:
<convert converter="bean" match="com.example.Person"> <param name="exclude" value="property1, property2"/>//黑名单法则,不转换property1、2。 </convert> |
通常使用JavaBean作为远程Java方法的参数和返回值。如果远程Java方法的参数或返回值不是JavaBean实例,而是普通Object,则应该使用Object转换器。如下面的例子:
public class Person { private String name; public Person(String name){ this.name=name; } }//这是一个简单的POJO |
下面在dwr.xml文档中的设置:
<convert converter="object" match="com.example.Person"> <!-- 指定force=“true”,强制使用反射访问私有属性。--> <param name="force" value="true"/> </convert> |
那么如何设置Java类和js类之间的对应呢?可以使用javascript属性,例如下面的代码:
<convert converter="bean" match="com.example.Person" javascript="Person"/> |
接下来,讨论一种继承关系在DWR中的应用的:
public class Employee extends Person{ public void setEmployee(Employee emp){ ... } }//显然这个Employee继承了前面的Person。 |
dwr.xml代码:
<convert converter="bean" match="com.example.Employee" javascript="Employee"/> |
前端js代码:
var person=...// 从服务器返回一个Employee类的对象 person instanceof Employee// 毫无疑问,结果为true Remoted.addToFirends(person);// 前面的例子可以看出Remoted只能接受Person类参数,但是现在可以接受Employee,那就证明了一点,Employee是Person的子类。 |
但是,如果我们创建继承关系使用以下这种模式的时候:
var person1 = new Employee(); var person2 = new Person(); person1 instanceof Employee;//true person2 instanceof Employee;//false person1 instanceof Person;//fasle(unexcepted) person2 instanceof Person;//true |
显然,这不是我们所期待的,那么如果真的要实现这种继承要怎么办呢?我们可以通过js自带的“伪继承”机制来实现:
Employee.prototype = new Person();//prototype的继承模式,Employee的prototype属性执行Prototype对象。 Employee.prototype.constructor = Employee;//Prototype对象的constructor属性指向一个Constructor对象,而这个Constructor对象恰好就是这个函数自己。 ... person1 instanceof Employee;//true |
5.数组转换器(Array Converters)的使用
The array entries are less obvious. By default DWR marshalls arrays of all primitive types as well as arrays of all marshallable objects, which by default includes String and Date as noted previously.
默认情况下,数组转换器已经是打开的了,不过数组元素是基本数据类型还是字符串,或者是其他引用数据类型。因此,不需要在dwr.xml中添加任何额外的配置。而且因为数组的操作比较简单,那么就不再重复了。
6.集合类型转换器(Collection Converters)的使用
DWR提供好了两个集合转换器,两个转换器分别用于转换Map对象和Collection对象。DWR默认已经打开这两个集合转换器,因此无需在dwr.xml文件中配置。
如果实在要显示配置的的话,那么就需要使用如下的代码:
<convert convertor="collection" match="java.util.Collection"/> <convert convertor="map" match="java.util.Map"/> |
下面我们看文档的另外一段话:
There is no way of knowing the underlying type of the collection just using reflection. So the 2 converters above can convert from any collection to something meaningful in JavaScript, however there is no way to know what sort of Collection to convert to going the other way.
map和collection转换器有如下两个不足之处:
a.如果不使用泛型限制集合元素的类型,那么DWR无法确定集合元素的类型,从而,无法把集合元素转变为有意义的js对象。
b.不能明确指定集合的类型,只能使用基于接口的类型转换。
不过集合可以不使用泛型来限制类型,但是可以通过使用dwr.xml中的<signatures.../>来声明集合元素的数据类型,从而可以让DWR能正确识别到集合元素的类型。如果使用泛型来限制集合元素类型将会更加简洁。
7.方法声明(signatures)的使用
The signatures section is used to enable resolution of the types stored in Collections when specific generic types are not being used.
因为DWR使用反射来实现集合元素的类型获取,但是有时候并不能很好的实现。特别是如同下面的这种情况:
public class Check { public List<?> setLotteryResults(List<?> whatDoIContain) { ... } } |
那么这个时候就可以使用signatures来帮助实现“类泛型”的方法:
<signatures> <![CDATA[ import java.util.List; import com.example.Check; Check.setLotteryResults(List<Integer> nos); ]]> </signatures> |
请记住:signatures元素的使用是在DWR没有办法实现泛型转换的基础上进行的。