技术开发 频道

WebService开发常见问题

  【IT168 技术文档】任何问题都需要从它的根源说起,所以简单说一下WebService的工作原理。客户端调用一个WebService的方法,首先需要将方法名和需要传递的参数包装成XML(也就是SOAP包),通常是通过HTTP传递到服务器端,然后服务器端解析这段XML,得到被调用方法名称和传递来的参数,进而调用WebService实例的相应方法。方法执行完成之后,将返回的结果再包装成XML(SOAP响应)发送到客户端,客户端解析这段XML,进而得到返回结果。这里关键的地方在于中间加入了对象和XML相互转换的过程。

  问题一:WebService与方法重载

  首先说明,WebService不支持方法重载。下面举例说明。

  例如定义如下WebService接口:

1 @WebService
2 public interface IHello {
3         @WebMethod
4         public String sayHello(int id);
5
6         @WebMethod
7         public String sayHello(String name);
8         
9         @WebMethod
10         public String sayHello2(int id);
11
12         @WebMethod
13         public String sayHello2(int id, String name);
14     }

  先来看方法sayHello(),如果客户端发送如下SOAP请求:

 

1 <soap:Envelope>
2         <soap:Body>
3             <sayHello>
4                 <arg0>11</arg0>
5             </sayHello>
6         </soap:Body>
7     </soap:Envelope>

  从SOAP请求我们可以看出客户端需要调用方法为sayHello(),所传递的参数为11,但是无法知道是整数的11,还是字符串"11",所以也就无法确定所调用的方法是哪一个。

  接下来看一下sayHello2(),如果客户端传递的参数只包括一个id值,例如:

1 <soap:Envelope>
2         <soap:Body>
3             <sayHello2>
4                 <arg0>1</arg0>
5             </sayHello2>
6         </soap:Body>
7     </soap:Envelope>

  还是无法判断调用的是哪个方法,因为可以理解为客户端传递的第二个参数为空(Null)。

  通常情况下,在发布一个含有重载方法的WebService时会有异常发生,或者当调用一个方法时,服务器端报告找不到相对应的方法。

  问题二:我的数据被修改了?

  先来看WebService接口:

1 @WebService
2     public interface IHello {
3     
4         @WebMethod
5         public String sayHello(IPerson person);
6     
7     }
8     
9     public interface IPerson {
10         ...
11     }
12     
13     public class Person implements IPerson{
14         ...
15     }

  这里需要注意的是WebService的方法sayHello()的参数是一个接口,而不是一个具体类(例如Aegis绑定就允许直接发布这样的WebService)。在客户端调用sayHello()的时候传递一个Person对象,它实现了IPerson接口。经过中间一系列的XML和对象之间的转换过程,服务器端得到的只是一个实现了IPerson接口的实例,它不一定就是一个Person对象,如果要强制将其转换为Person,就有可能抛出异常。

  问题的根源在于Aegis将XML转换为Java对象是通过Proxy或CGlib这类的工具生成一个“代理类”实现IPerson接口,然后创建这个代理类的一个实例,那它肯定不是一个Person了。

  问题三:循环引用

  还是先来看一个例子。下面是WebService的接口:

1 @WebService
2     public interface IHello {
3     
4         @WebMethod
5         public String sayHello(Teacher teacher);
6     
7     }
8     
9     public class Teacher {
10         private Student[] students;
11         
12         //getters and setters
13         ...
14     }
15     
16     public class Student {
17         private Teacher teacher;
18         
19         //getters and setters
20         ...
21     }

  请注意,Teacher和Student是一对多的“双向”关系。在这种情况下,我们可以想一下如何将一个Teacher对象转换成一段XML?

  您可能想到下面的答案:

1 <teacher>
2         <students>
3             <teacher>
4                 <students>
5                     <teacher>
6                         <students>
7                             ...
8                         </students>
9                         ...
10                     </teacher>
11                 </students>
12                 ...
13             </teacher>
14         </students>
15         
16         <students>
17             ...
18         </students>
19         ...
20     </teacher>

  看到了吧,XML竟然也会进入“死循环”。问题的根源在于对象之间的循环引用。这种问题通常在客户端发送WebService请求之前就会抛出异常,因为无法将这个对象转换为可传输的XML。

  问题四:庞然大物

  还是先看一个例子,下面是WebService的接口:

1 @WebService
2     public interface IHello {
3     
4         @WebMethod
5         public String sayHello(Student[] students);
6     
7     }
8     
9     public class Teacher {
10         ...
11     }
12     
13     public class Student {
14         private Teacher teacher;
15         
16         //getters and setters
17         ...
18     }

  这个方法接收一个Student数组,包含成百上千个Student,与上面例子不同的是Student和Teacher现在是多对一的单向关系,所以不会有“循环引用”的问题。假设所有这些Student的Teacher是一个人。我们试着将这个Student数组对象转换为一段XML,如下:

1 <student>
2     <teacher>
3                 ...
4         </teacher>
5     </student>
6 <student>
7     <teacher>
8                 ...
9         </teacher>
10     </student>
11     ...

  问题出来了,看到了没有,每个Student节点下面都有一个Teacher节点,当这段XML被接收方转换为Student数组时,每个学生都有了一个自己的老师,Teacher对象被复制了成百上千次,经过这么一个转换--传输--转换的过程,这个数组对象真的成了一个“庞然大物”。

  问题的根源在于Student和Teacher之间的关系是多对一,当传送“多”方时,“一”方有可能会被复制多次。从而占用大量网络传输带宽和内存。在这里参数不一定非要是一个集合或者数组,例如ObjectA和ObjectB都有一个对ObjectC的引用,经过SOAP传送过后,ObjectC就由一个变成两个了,分别属于ObjectA和ObjectB,而不再是共享一个ObjectC了。

2
相关文章