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.xml12345678910111213141516
<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.xml12345
<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.xml12345678
<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