用Axis2实现Web Service,虽然可以将POJO类放在axis2\WEB-INF\pojo目录中直接发布成Web Service,这样做不需要进行任何配置,但这些POJO类不能在任何包中。这似乎有些不方便,为此,Axis2也允许将带包的POJO类发布成Web Service。
先实现一个POJO类,代码如下:
package service;
public class MyService
{
public String get Greeting(String name)
{
return "您好 " + name;
}
public void update(String data)
{
System.out.println("<" + data + ">已经更新");
}
}
这个类有两个方法,这两个方法都需要发布成Web Service方法。这种方式和直接放在pojo目录中的POJO类不同。要想将MyService类发布成Web Service,需要一个services.xml文件,这个文件需要放在META-INF目录中,该文件的内容如下:
Web Service例子
service.MyService
class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/> class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver"/> 其中 http://localhost:8080/axis2/services/m yService?wsdl 其中name属性名就是上面URL中"?"和"/"之间的部分。 使用这种方式发布WebService,必须打包成.aar文件,..aar文件实际上就是改变了扩展名的.jar文件。在现在建立了两个文件:MyService.java和services.xml。将MyService.java编译,生成MyService.class。services.xml和MyService.class文件的位置如下: D:\ws\service\MyService.class D:\ws\META-INF\services.xml 在windows控制台中进入ws目录,并输入如下的命令生成.aar文件(实际上,.jar文件也可以发布webservice,但axis2官方文档中建议使用.aar文件发布webservice):jar cvf ws.aar . 最后将ws.aar文件复制到 另外services.xml文件中也可以直接指定WebService类的方法,如可以用下面的配置代码来发布WebService: Web Service例子 service.MyService class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver"/> 上面的配置代码前面的部分和以前的services.xml文件的内容相同,但后面使用了 如果想发布多个WebService,可以使用 package service public class MyService1 { public String getName() { return "bill"; } } 在services.xml文件中可以使用如下的配置代码来配置MyService和MyService1类: Web Service例子 service.MyService class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/> class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver"/> Web Service例子 service.MyService1 class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/> class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver"/> 在《WebService大讲堂之Axis2(2):复合类型数据的传递》中讲过,如果要传递二进制文件(如图像、音频文件等),可以使用byte[]作为数据类型进行传递,然后客户端使用RPC方式进行调用。这样做只是其中的一种方法,除此之外,在客户端还可以使用wsdl2java命令生成相应的stub类来调用WebService,wsdl2java命令的用法详见《WebService大讲堂之Axis2(1):用POJO实现0配置的WebService》。 WebService类中包含byte[]类型参数的方法在wsdl2java生成的stub类中对应的数据类型不再是byte[]类型,而是javax.activation.DataHandler。DataHandler类是专门用来映射WebService二进制类型的。 在WebService类中除了可以使用byte[]作为传输二进制的数据类型外,也可以使用javax.activation.DataHandler作为数据类型。不管是使用byte[],还是使用 javax.activation.DataHandler作为WebService方法的数据类型,使用wsdl2java命令生成的stub类中相应方法的类型都是javax.activation.DataHandler。而象使用.net、delphi 生成的stub类的相应方法类型都是byte[]。这是由于javax.activation.DataHandler类是Java特有的,对于其他语言和技术来说,并不认识javax.activation.DataHandler类,因此,也只有使用最原始的byte[]了。 下面是一个上传二进制文件的例子,WebService类的代码如下: package service; import java.io.InputStream; import java.io.OutputStream; import java.io.FileOutputStream; import javax.activation.DataHandler; public class FileService { // 使用byte[]类型参数上传二进制文件 public boolean uploadWithByte(byte[] file, String filename) { FileOutputStream fos =null; try { fos =new FileOutputStream(filename); fos.write(file); fos.close(); } catch (Exception e) { return false; } finally { if (fos !=null) { try { fos.close(); } catch (Exception e) { } } } return true; } private void writeInputStream ToFile(InputStream is, OutputStream os) throws Exception { int n = 0; byte[] buffer =new byte[8192]; while((n = is.read(buffer)) > 0) { os.write(buffer, 0, n); } } // 使用DataHandler类型参数上传文件 public boolean uploadWithDataHandler(DataHandler file, String filenam e) { FileOutputStream fos =null; try { fos =new FileOutputStream(filename); // 可通过DataHandler类的getInputStream方法读取上传数据 writeInputStream ToFile(file.getInputStream(), fos); fos.close(); } catch (Exception e) { return false; } finally { if (fos !=null) { try { fos.close(); } catch (Exception e) { } } } return true; } } 上面代码在services.xml文件的配置代码如下: 文件服务 service.FileService class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/> 如果使用wsdl2java命令生成调用Java客户端代码,则需要创建DataHandler类的对象实例,代码如下: DataHandler dh =new DataHandler(new FileDataSource(imagePath)); wsdl2java命令会为每一个方法生成一个封装方法参数的类,类名为方法名(第一个字符大写),如uploadWithByte方法生成的类名为UploadWithByte。如果要设置file参数的值,可以使用UploadWithByte类的setFile方法,代码如下: UploadWithByte uwb =new UPloadWithByte(); uwb.setFile(dh); 最后是调用uploadWithByte方法,代码如下(FileServiceStub为wsdl2java生成的stub类名): FileServiceStub fss =new FileServiceStub(); fss.uploadWithByte(uwb); 如果使用C#调用FileService,则file参数类型均为byte[],代码如下: Mem oryStream m s = new MemoryStream(); Bit m ap bit m ap =new Bit m ap(picUpdateIm age.Image); bit m ap.Save(m s, System.Drawing.Imaging.ImageFormat.Jpeg); service.fileService fs =new WSC.service.fileService(); fs.uploadWithDataHandler(ms.ToArray()); fs.uploadWithByte(m s.ToArray()); 其中picUpdateImage为c#中加载图像文件的picturebox控件。 WebService给人最直观的感觉就是由一个个方法组成,并在客户端通过SOAP协议调用这些方法。这些方法可能有返回值,也可能没有返回值。虽然这样可以完成一些工具,但这些被调用的方法是孤立的,当一个方法被调用后,在其他的方法中无法获得这个方法调用后的状态,也就是说无法保留状态。 读者可以想象,这对于一个完整的应用程序,无法保留状态,就意味着只依靠WebService 很难完成全部的工作。例如,一个完整的应用系统都需要进行登录,这在Web应用中使用Session来保存用户登录状态,而如果用WebService的方法来进行登录处理,无法保存登录状态是非常令人尴尬的。当然,这也可以通过其他的方法来解决,如在服务端使用static变量来保存用户状态,并发送一个id到客户端,通过在服务端和客户端传递这个id来取得相应的用 户状态。这非常类似于Web应用中通过Session和Cookie来管理用户状态。但这就需要由开发人员做很多工作,不过幸好Axis2为我们提供了WebService状态管理的功能。 使用Axis2来管理WebService的状态基本上对于开发人员是透明的。在WebService 类需要使用org.apache.axis2.context.MessageContext和 org.apache.axis2.context.ServiceContext类来保存与获得保存在服务端的状态信息,这有些象使用HttpSession接口的getAttribute和setAttribute方法获得与设置Session域属性。 除此之外,还需要修改services.xml文件的内容,为 在客户端需要使用setManageSession(true)打开Session管理功能。 综上所述,实现同一个WebService的Session管理需要如下三步: 1. 使用MessageContext和ServiceContext获得与设置key-value对。 2. 为要进行Session管理的WebService类所对应的 3. 在客户端使用setManageSession(true)打开Session管理功能。 下面是一个在同一个WebService类中管理Session的例子。 先建立一个WebService类,代码如下: package service; import org.apache.axis2.context.ServiceContext; import org.apache.axis2.context.MessageContext; public class LoginService { public boolean login(String username, String password) { if("bill".equals(username) && "1234".equals(password)) { // 第1步:设置key-value对 MessageContext m c = MessageContext.getCurrentMessageContext(); ServiceContext sc = m c.getServiceContext(); sc.setProperty("login", "成功登录"); return true; } else { return false; } } public String getLoginMsg() { // 第1步:获得key-value对中的value MessageContext m c = MessageContext.getCurrentMessageContext(); ServiceContext sc = m c.getServiceContext(); return (String)sc.getProperty("login"); } } 在LoginService类中有两个方法:login和getLoginMsg,如果login方法登录成功,会将“成功登录”字符串保存在ServiceContext对象中。如果在login方法返回true后调用getLoginMsg方法,就会返回“成功登录”。 下面是LoginService类的配置代码(services.xml): 登录服务 service.LoginService class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/> 使用如下的命令生成客户端使用的stub类: %AXIS2_HOME%\bin\wsdl2java -uri http://localhost:8080/axis2/services/loginSe rvice?wsdl -p client -s -o stub 在stub\src\client目录中生成了一个LoginServiceStub.java类,在该类中找到如下的构造句方法: public LoginServiceStub(org.apache.axis2.context.ConfigurationContext configura tionContext, https://www.doczj.com/doc/8b12094984.html,ng.String targetEndpoint, boolean useSeparateListener) throws org.apache.axis2.AxisFault { _serviceClient.getOptions().setSoapVersionURI( org.apache.axiom.soap.SOAP12Constants.SOAP_ENVELOP E_NAMESPACE_URI); } 在该方法中最后添加如下的代码: // 第3步:打开客户端的Session管理功能 _serviceClient.getOptions().setManageSession(true); 下面的客户端代码使用LoginServiceStub对象访问了刚才建立的WebService:LoginServiceStub stub =new LoginServiceStub(); LoginServiceStub.Login login =new LoginServiceStub.Login(); login.setUsername("bill"); login.setPassword("1234"); if(stub.login(login).local_return) { System.out.println(stub.getLoginMsg().local_return); } 运行上面的代码后,会输出“成功登录”信息。 在《WebService大讲堂之Axis2(5):会话(Session)管理》一文中介绍了如何使用Axis2来管理同一个服务的会话,但对于一个复杂的系统,不可能只有一个WebService服务,例如,至少会有一个管理用户的WebService(用户登录和注册)以及处理业务的WebService。象这种情况,就必须在多个WebService服务之间共享会话状态,也称为跨服务会话(Session)管理。实现跨服务会话管理与实现同一个服务的会话管理的步骤类似,但仍然有一些差别,实现跨服务会话管理的步骤如下: 实现跨服务的Session管理需要如下三步: 1. 使用MessageContext和ServiceGroupContext获得与设置key-value对。 2. 为要进行Session管理的WebService类所对应的 3. 在客户端使用setManageSession(true)打开Session管理功能。 从上面的步骤可以看出,实现跨服务会话管理与实现同一个服务的会话管理在前两步上存在着差异,而第3步是完全一样的。下面是一个跨服务的会话管理的实例。在这个例子中有两个WebService类:LoginService和SearchService,代码如下: LoginService.java package service; import org.apache.axis2.context.MessageContext; import org.apache.axis2.context.ServiceGroupContext; public class LoginService { public boolean login(String username, String password) { if("bill".equals(username) && "1234".equals(password)) { // 第1步:设置key-value对 MessageContext m c = MessageContext.getCurrentMessageContext(); ServiceGroupContext sgc = m c.getServiceGroupContext(); sgc.setProperty("login", "成功登录"); return true; } else { return false; } } public String getLoginMsg() { // 第1步:获得key-value对中的value MessageContext m c = MessageContext.getCurrentMessageContext(); ServiceGroupContext sgc = m c.getServiceGroupContext(); return (String)sgc.getProperty("login"); } } SearchService.java package service; import org.apache.axis2.context.MessageContext; import org.apache.axis2.context.ServiceGroupContext; public class SearchService { public String findByName(String name) { // 第1步:获得key-value对中的value MessageContext m c = MessageContext.getCurrentMessageContext(); ServiceGroupContext sgc = m c.getServiceGroupContext(); if (sgc.getProperty("login") !=null) return "找到的数据<" + name + ">"; else return "用户未登录"; } } services.xml文件中的配置代码如下: 登录服务 service.LoginService class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/> 搜索服务 service.SearchService class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/> 第3步与《WebService大讲堂之Axis2(5):会话(Session)管理》一文中介绍的方法类似。 下面是使用两个stub类的对象实例访问上面实现的两个WebService的客户端代码:LoginServiceStub stub =new LoginServiceStub(); LoginServiceStub.Login login =new LoginServiceStub.Login(); login.setUsername("bill"); login.setPassword("1234"); if(stub.login(login).local_return) { System.out.println(stub.getLoginMsg().local_return); SearchServiceStub searchStub =new SearchServiceStub(); SearchServiceStub.FindByName fbn =new SearchServiceStub.FindByName(); fbn.setNam e("abc"); System.out.println(searchStub.findByName(fbn).local_return); } 在执行上面的代码后,将输出如下的信息: 成功登录 找到的数据 读者可以将scope属性值改成transportsession,看看会输出什么! 实际上,Axis2的会话管理也是通过Cookie实现的,与Web应用中的Session管理类似。如果读者使用C#访问支持会话(在同一个服务中的会话管理)的WebService,需要指定一个CookieContainer对象,代码如下: service.loginService ls =new service.loginService(); https://www.doczj.com/doc/8b12094984.html,.CookieContainer cc =new https://www.doczj.com/doc/8b12094984.html,.CookieContainer(); ls.CookieContainer = cc; bool r, rs; ls.login("bill", "1234", out @r, out rs); if (r) { MessageBox.Show(ls.getLoginMsg().@return); } 如果是访问跨服务的支持会话的WebService,则不需要指定CookieContainer对象,代码如下: service.loginService ls =new service.loginService(); bool r, rs; ls.login("bill", "1234", out @r, out rs); if (r) { service1.searchService ss =new service1.searchService(); MessageBox.Show(ss.findByName("abc")); } 如果读者使用delphi(本文使用的是delphi2009,其他的delphi版本请读者自行测试)调用支持会话的WebService时有一些差别。经笔者测试,使用delphi调用WebService,将scope属性值设为transportsession和application都可以实现跨服务的会话管理,这一点和Java与C#不同,Java和C#必须将scope属性值设为application才支持跨服务会话管理。在delphi中不需要象C#指定一个CookieContainer或其他类似的对象,而只需要象访问普通的WebService一样访问支持会话的WebService即可。 在现今的Web应用中经常使用Spring框架来装载JavaBean。如果要想将某些在Spring中装配的JavaBean发布成WebService,使用Axis2的Spring感知功能是非常容易做到的。 在本文的例子中,除了 录>\webapps\axis2\WEB-INF\lib目录中。 下面先建立一个JavaBean(该JavaBean最终要被发布成WebService),代码如下: package service; import entity.Person; public class SpringService { private String name; private String job; public void setName(String name) { this.nam e = name; } public void setJob(String job) { this.job = job; } public Person getPerson() { Person person =new Person(); person.setNam e(name); person.setJob(job); return person; } public String get Greeting(String name) { return "hello " + name; } } 其中Person也是一个JavaBean,代码如下:package entity; public class Person