浅析基于AXIS框架的WebService

时间:2022-06-21 08:25:03

一、写在前面

  之前做项目用到了基于Axis的WebService,为了更进一步的理解和记忆,在这里通过代码实践和源码分析来完整的做一遍Axis的WebService以及对应的客户端调用实践,并和其它的方式的接口做一些对比。

二、基于Axis的WebService和HTTP接口的优劣

  本质上而言,Axis的WebService在数据传输时用的就是基于HTTP协议的POST请求,调用WebService接口实际上就是在HTTP header里添加SOAPAction头,然后请求体里发送的是XML格式的数据,即SOAP报文,内部包括请求方法、请求参数等信息。

  客户端可以通过服务端提供的服务类的wsdl文件在本地生成一个服务类代理,使得客户端调用服务端的接口可以通过new一个本地代理类对象并调用实例方法来实现;比如服务代理类叫ATestProxy,那么调用WebService接口可以直接是

ATestProxy proxy = new ATestProxy();
Object result = proxy.servicePart(3);
/**
这里的servicePart就是服务端的服务类提供的接口,它的原理是
proxy.servicePart方法内部最终会通过HTTP POST请求发送数据,
此请求会被服务器中间件转发给Axis的Servlet,然后Servlet解析
请求的请求体,通过请求体提供的服务类、服务类方法、参数等
数据定位到服务类的具体方法,从而在Servlet里创建该服务类对象并
调用对应方法,然后返回值通过HTTP Response返回给客户端,
客户端又将Response里的响应体解析为变量或类对象
*/

WebService相比自写的HTTP请求好处有:

  1.容易定位使用位置,右键代理类方法查找引用即可;2.使用方便,不需要自己再构造HttpURLConnection对象;

WebService相比自写的HTTP请求劣处有:

  1.性能损耗,分析自动生成的服务代理类可以发现一个简单的HTTP请求被分成了很多步骤(光是产生的客户端类就有五个),从数据包装到真正发送数据经过了多个类的协调工作,这显然是比直接通过HTTP请求发送SOAP报文要慢的。2.不够灵活,勉强算是一个不是劣处的劣处吧。

  经过对比,个人认为,如果一个接口是可能频繁被调用的,那么一定不要用WebService;如果是内部系统的接口则用WebService是不错的选择。

三、创建WebService服务端

1.先创建一个简单的webapp项目,创建完毕后src及web.xml如图:

2.接下来在这个基础上创建一个WebService的服务类及供外部调用的public访问权限的方法,创建好后src和代码如图:

3.为此webapp创建基于Axis的WebService:
3.1.右键SerialCodeGenerator->New->Web Service->{在此步的Service Implementation填上silentdoer.web.services.SerialCodeGenerator,然后更改Configuration中的Server Runtime等配置,点击next;
3.2.勾选要发布的Methods,然后next,点击Start Server;
3.3.此webapp会添加进之前配置的tomcat Server里并启动tomcat,在WEB-INF目录下会生成server-config.wsdd文件(还有其它文件但是都不很重要),以及web.xml里会自动配置<servlet-name>AxisServlet</servlet-name>等配套的配置。
在server-config.wsdd里有如下配置:
<ns1:service name="SerialCodeGenerator" provider="java:RPC" style="wrapped" use="literal">
<!--这段配置可以注释-->
<!--<ns2:operation name="getNewStudentCode" qname="ns1:getNewStudentCode" returnQName="ns1:getNewStudentCodeReturn" returnType="xsd:long" soapAction="" xmlns:ns1="http://services.web.silentdoer" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns2="http://xml.apache.org/axis/wsdd/"/>-->
<!--可以将getNewStudentCode改为*则表示开放SerialCodeGenerator中所有的public的方法-->
<ns1:parameter name="allowedMethods" value="getNewStudentCode"/>
<ns1:parameter name="typeMappingVersion" value="1.2"/>
<ns1:parameter name="wsdlPortType" value="SerialCodeGenerator"/>
<ns1:parameter name="className" value="silentdoer.web.services.SerialCodeGenerator"/>
<ns1:parameter name="wsdlServicePort" value="SerialCodeGenerator"/>
<ns1:parameter name="schemaQualified" value="http://services.web.silentdoer"/>
<ns1:parameter name="wsdlTargetNamespace" value="http://services.web.silentdoer"/>
<ns1:parameter name="wsdlServiceElement" value="SerialCodeGeneratorService"/>
</ns1:service>
3.4.启动webapp所在的tomcat server后,在浏览器中输入:http://localhost:8080/Demo_WebServiceInWebapp/services/SerialCodeGenerator?wsdl即可获得此port对应的wsdl配置文件;这个配置文件的生成是通过调用wsdl引擎生成的,后面客户端通过wsdl文件生成Java代码也是通过这个引擎实现的;它的原理就是Axis的Servlet收到请求后会判断路径URI和查询参数,发现是SerialCodeGenerator?wsdl则匹配wsdd文件中的配置,匹配成功获得对于发布类的一系列信息[类名、public方法名、参数信息等等](可通过反射实现),然后在将这些信息作为参数调用wsdl引擎的映射方法,wsdl引擎返回wsdl配置信息,这个过程其实就是参数映射或说数据转换,只要理解好映射规则咱们自己也能写个wsdl引擎,比如假设传入字符串参数SerialCodeGenerator,返回<wsdl:portType name="SerialCodeGenerator">..</wsdl:portType>;
3.5.Java Axis框架本质上也是一个基于Servlet的处理服务,服务器中间件通过url-pattern将符合path/services/*的请求交给Axis的org.apache.axis.transport.http.AxisServlet处理,而客户端调用WebService时最终还是一个HTTP请求,pathinfo里以services开头,请求体里是soap报文,
在Header里有个SOAPAction用于区分普通的HTTP请求;对于服务器中间件而言这就是个普通的HTTP请求并将它转发给AxisServlet处理,AxisServlet内部会解析soap报文,提取出port、part、parameters等关键信息然后匹配后台的代码,匹配到则调用对应的方法并将返回值也封装成soap报文写在响应体里从而完成对客户端的服务。
3.6.客户端会生成一个和服务端开放接口具有相同方法签名的接口以及其实现类的stub,客户端通过stub对象调用本地方法就能够自动请求服务端调用对应的方法,然后返回结果给客户端的stub的对应方法,比如客户端调用stub.getCode()方法,在getCode()方法里会间接的构造soap报文,报文中的method就是getCode,其它参数也传递进来,然后通过HTTP请求发送此soap报文,然后服务端处理后返回数据,在getCode()里继续处理返回的数据并反序列化成对象然后返回给stub.getCode()的调用者。
3.7.在webapp里创建axis的webservice可以自己添加jar包和配置web.xml和server-config.wsdd也能实现在webapp里添加基于Axis框架的WebService服务。
3.8.接口方法的参数或返回值是一个JavaBean

四、创建WebService的测试客户端

1.同样的先创建一个项目,名字比如叫WebServiceTest;
2.右键src-new-WebService Client,然后在service definition里填写http://localhost:8080/Demo_WebServiceInWebapp/services/SerialCodeGenerator?wsdl;
3.点击finish,然后wsdl引擎会根据service definition中获得的wsdl描述信息生成相应的Java代码,如图:

4.这5个.Java文件的对应关系是SerialCodeGenerator是接口,里面的方法签名和服务端的一致,SerialCodeGeneratorProxy和SerialCodeGeneratorSoapBindingStub均继承自该接口,而Proxy是stub的代理;而SerialCodeGeneratorServiceLocator继承自SerialCodeGeneratorService,SerialCodeGeneratorService又继承自Service,这个Service对象可以用来创建Call对象,Call对象是WebServiceClient里真正调用请求服务端的比较底层的组成部分,后面会演示直接用Call对象来调用WebService服务(Call下面还可以继续深挖出HTTP请求-TCP请求等),SerialCodeGeneratorServiceLocator同时还存储了webservice请求的url,在本例中是:http://localhost:8080/Demo_WebServiceInWebapp/services/SerialCodeGenerator;(用UML)
5.main方法里调用,待添加;(客户端其实只需要知道请求的url、soap报文中的必要数据,如ser参数,方法名、参数等信息再加上SOAPAction的Header就可以用HTTP请求来实现调用webservice服务了);

6.如果服务端和客户端的JDK版本不一致则还可以直接通过HTTP请求发送soap报文。

7.客户端直接用Call类来完成webservice的调用

String endpoint="http://localhost:8080/SecondSpringMvcProj/services/HelloWebService";

        Call call = new Call(endpoint);

        // 对于复杂对象需要设置返回类型
call.setReturnClass(Student.class);
call.setOperationName("getStudent");
Student student = (Student)call.invoke(new Object[] {"uuuu"});
System.out.println(student.getName());

五、添加复杂类型

如果参数或返回值是自定义类型,则需要在server-config.wsdd的对应service内添加

<beanMapping qname="myNSD:Student" xmlns:myNSD="urn:Student"
languageSpecificType="java:silentdoer.web.pojo.Student"/>,否则复杂类型将无法正确被传输。

六、总结

如果是系统内部直接的接口用webservice还是不错的,毕竟用起来很方便,而且很多代码不用自己写;但是如果接口是与外部系统对接,且要求效率较高,灵活性较好则应该用HTTP请求;