Webservice之实现

来源:互联网 发布:鲁班软件 北京 编辑:程序博客网 时间:2024/06/05 05:03
<pre name="code" class="java">

webservice定义了一种不同系统之间功能调用的标准,也就是RPC调用。在j2ee中,ejb提供了这种分布式的RPC,原理是用jndi发布ejb远程无状态bean,客户端得到bean的引用完成方法调用,这种方式对于同是java系统的调用非常的方便,但对于异构系统的访问就非常的不便,特别是在互联网中,基于ejb调用的sockect连接可能无法穿过企业防火墙以完成方法的调用。

  webservice提供了一种标准的不同系统之间的调用,基于soap协议,底层可以选用http传输协议(可以穿过防火墙),而且中间信息传递是 xml格式的,消息体的打包和解析十分的方便,可以说ws就是基本于xml、http的远程RPC调用。

  java下常用的ws框架有axis2、xfire等,xfire已经升级为cxf,相对axis来说要,使用要简单得多,而且cxf与spring集成非常的紧密,下面我以具体实例来建立ws服务器,并写出了在cxf,axis以及php中的调用示例,本文中的示例可直接应用于生产环境。

  首先写出ws的接口,这里要用到外观模式。

1234567
@WebServicepublic interface IWebServiceFacade {  public String sayHello(@WebParam(name="uname")String name);  public User getFirstUserByParent(@WebParam(name="userParam")User u);  public @WebResult(partName="users")List getAllUsers();  public int add(@WebParam(name="num1")String number1, @WebParam(name="num2")int number2);}

  为了让示例具有通用性,我定义了一些典型的方法,比如:方法接收对象类型参数,返加对象类型结果,返回集合类型结果,有两个参数的情况等等。

  该接口的实现代码如下(注意,bizService由spring注入):

123456789101112131415161718192021222324252627
@WebService(endpointInterface="com.my.webservice.IWebServiceFacade")public class WebServiceFacadeImpl implements IWebServiceFacade {  //本地业务接口  private IBizService bizService;  @Override  public String sayHello(String name) {      return "Hello," + name;  }  public int add(String number1, int number2) {      return (Integer.parseInt(number1) + number2);  }  @Override  public List getAllUsers() {      //此处应调用 bizService方法完成,作为示例,我直接返回模拟数据      List users = new ArrayList();      users.add(new User(1,"张三"));      users.add(new User(2,"李四"));      users.add(new User(3,"王五"));      return users;  }  @Override  public User getFirstUserByParent(User u) {      //此处应调用 bizService方法完成,作为示例,我直接返回模拟数据      System.out.println("example param user ,name is" + u.getUserName() + ", birth is:" + u.getBirthday().toLocaleString());      return new User(1,u.getUserName() + "--admin");  }}

  至此,服务器端的代码已经完成了,但还需要一定的配置,在服务器上的配置代码片断如下(web.xml):

web.xml
12345678910111213141516
<servlet>  <servlet-name>CXFServlet</servlet-name>  <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>  <load-on-startup>1</load-on-startup></servlet><servlet-mapping>  <servlet-name>CXFServlet</servlet-name>  <url-pattern>/services/*</url-pattern></servlet-mapping><listener>  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><context-param>  <param-name>contextConfigLocation</param-name>  <param-value>/WEB-INF/classes/app_ws_server.xml</param-value></context-param>

  app_ws_server.xml中(这是一个spring配置文件),关键配置代码如下:

app_ws_server.xml
12345
<import resource="classpath:META-INF/cxf/cxf.xml" /><import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" /><import resource="classpath:META-INF/cxf/cxf-servlet.xml" /><jaxws:endpoint id="myWebService" address="/myWebService"        implementor="com.my.webservice.WebServiceFacadeImpl"></jaxws:endpoint>

  重新启动服务器,如果一切正常的话,在浏览器地址栏中输入http://localhost:8088/web_service_server/services/myWebService?wsdl就可以看到wsdl文件,有了wsdl,其它客户端的调用将不存在问题。

  下面,我将写出不同客户端对上面的ws调用代码

  最为简单的是cxf自己的客户端调用方式,先写一篇spring配置文件,将要调用的客户端写入:

ws-client.xml
12345678
<bean id="wsClient" class="com.my.webservice.IWebServiceFacade"  factory-bean="wsClientProxy" factory-method="create" /><bean id="wsClientProxy" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">  <property name="serviceClass" value="com.my.webservice.IWebServiceFacade" />  <property name="address"      value="http://localhost:8088/web_service_server/services/myWebService" /></bean>

  cxf的测试代码如下,wsClient由测试框架自动注入,这种方式应该是在java下调用ws服务最简单的方法,和本地方法调用没有任何区别。

1234567891011121314151617181920212223242526272829303132333435363738
public class WebServiceTest extends      AbstractDependencyInjectionSpringContextTests {  private IWebServiceFacade wsClient;  public void testSayHello() {      System.out.println("====================");      String result = wsClient.sayHello("lovo");      System.out.println(result);      System.out.println("add:" + wsClient.add("5", 3));      User u = wsClient.getFirstUserByParent(new User(1,"刘江华"));      System.out.println(u.getUserName());      List<User> users = wsClient.getAllUsers();      System.out.println("users List size: " + users.size());      System.out.println(users.get(0).getUserName() );      System.out.println(users.get(0).getBirthday());  }  @Override  protected String[] getConfigLocations() {      setAutowireMode(AUTOWIRE_BY_NAME);      return new String[]{"classpath:ws-client.xml"};  }  public void setWsClient(IWebServiceFacade wsClient) {      this.wsClient = wsClient;  }}

  鉴于axis使用比较广泛,我写出了相应的调用代码,axis下调用代码相对来说要繁琐些,特别要注意在传对象参数时需要call.setProperty(AxisEngine.PROP_DOMULTIREFS, false),关闭multirefs的生成(通过http抓包工具可以观察向服务器提交的请求),调用代码示例如下:

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
String endpoint = "http://localhost:8088/web_service_server/services/myWebService";      Service service = new Service();      //测试sayHello      Call call = (Call) service.createCall();      call.setTargetEndpointAddress(new java.net.URL(endpoint));      call.setOperationName(new QName("http://webservice.my.com/", "sayHello"));      call.addParameter("uname", XMLType.SOAP_STRING, ParameterMode.IN);      call.setReturnType(XMLType.SOAP_STRING);      String result = (String) call.invoke(new Object[] {"good"});      System.out.println(result);      //测试add      call = (Call) service.createCall();      call.setTargetEndpointAddress(new java.net.URL(endpoint));      call.setOperationName(new QName("http://webservice.my.com/", "add"));      call.addParameter("num1", XMLType.SOAP_STRING, ParameterMode.IN);      call.addParameter("num2", XMLType.SOAP_INT, ParameterMode.IN);      call.setReturnType(XMLType.SOAP_INT);      //参数顺序和addParameter一致      int r = (Integer) call.invoke(new Object[] {"5", "3"});      System.out.println(r);      //测试得到所有用户      call = (Call) service.createCall();      call.setTargetEndpointAddress(new java.net.URL(endpoint));      call.setOperationName(new QName("http://webservice.my.com/", "getAllUsers"));      call.setReturnType(org.apache.axis.encoding.XMLType.SOAP_VECTOR);      Vector v = (Vector) call.invoke(new Object[] {});      for (int i = 0; i< v.size(); i++) {          Vector v2 = (Vector)v.get(i);          for (int j = 0; j < v2.size(); j++) {              System.out.print(v2.get(j) + "  ");          }          System.out.println();      }      //传入用户对象,得到一个用户      call = (Call) service.createCall();      call.setTargetEndpointAddress(new java.net.URL(endpoint));      call.setOperationName(new QName("http://webservice.my.com/", "getFirstUserByParent"));      QName qn = new QName("http://webservice.my.com/", "user" );      call.addParameter("userParam",qn, ParameterMode.IN);      call.setProperty(AxisEngine.PROP_DOMULTIREFS, false);      call.registerTypeMapping(User.class, qn, new BeanSerializerFactory(User.class, qn), null);      call.setReturnType(org.apache.axis.encoding.XMLType.SOAP_VECTOR);      User u = new User(1," 刘江华 ");      v = (Vector) call.invoke(new Object[] {u});      System.out.println(v);

  最后,给出非java系统的一个客户端调用示例,php的调用代码如下(要注意,php默认情况下没有开启对soap的支持,需求修改php的配置文件):

1234567891011121314151617181920212223242526
<?php$ws = "http://localhost:8088/web_service_server/services/myWebService?wsdl";$client = new SoapClient($ws, array ('trace' => 1, 'uri' => 'http://webservice.my.com/' ));echo("sayHello ws call:");print_r( $client->sayHello(array('uname'=>'ljh')));echo("<br><br>");echo("add ws call: ");print_r( $client->add(array('num1'=>5,'num2'=>3)));echo("<br><br>");echo("getAllUsers ws call: ");print_r( $client->getAllUsers());echo("<br><br>");echo("getFirstUserByParent ws call: ");$aUser = array();$aUser['id']=1;$aUser['userName']='刘江华';$aUser['birthday']=date("Y-m-d");print_r($client->getFirstUserByParent(array('userParam'=>$aUser)));?>

  在客户端的调用中我们可以发现,php对ws的调用是相当的简单(请注意在构建SoapClient对象时传入了wsdl的路径,php就是根据wsdl来发送和接收服务器端数据的),正是由于这些原因,php在建站中被广泛使用(入门门槛比较低)。


                                             
0 0