加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

WebService实战

发布时间:2020-12-16 23:39:22 所属栏目:安全 来源:网络整理
导读:WebService实战 一、?????什么是WebService? 1.?????官方解释 Web service是一个平台独立的,松耦合的,自包含的、基于可编程的web的应用程序,可使用开放的XML标准来描述、发布、发现、协调和配置这些应用程序,用于开发分布式的互操作的应用程序。???????

WebService实战

一、?????什么是WebService?

1.?????官方解释

Web service是一个平台独立的,松耦合的,自包含的、基于可编程的web的应用程序,可使用开放的XML标准来描述、发布、发现、协调和配置这些应用程序,用于开发分布式的互操作的应用程序。????????????

---摘自百度百科

2.?????对概念的理解

  Web service翻译出来叫做Web服务,也就是说把具备某种功能的web服务开发出来,供大家使用,这样的web服务就是一个Web service。从这个角度讲,其实Web service是一个很泛的概念,只要是用于给其他系统提供服务的web系统,都可以称作为Web service,它并没有一种固定的标准。

  要提供一个web服务,最重要的就是输入和输出的统一,比如一个web系统提供"根据ip地址查找具体位置"的服务,那它首先需要规定查询的接口,从java的角度看就是设计一个暴露给外界的public方法,规定好其中的入参、出参。但是它不是一个RMI,不能像RMI那样暴露接口给对方,让调用方像调用本地类一样调用远程的web系统的某个类的方法,所以,光设计一套java的public方法是不行,还要给出一套规定,用于指明一个请求是调用哪个类的哪个方法,这就是实现web service的标准,这些标准通常由国际组织制定。

3.?????Web service与RMI的区别

  Web service提供的是远程服务,是通过通信的手段完成数据传递的,客户端准备入参等数据并发起请求,服务端负责处理这些并返回结果给客户端。

  RMI是java的远程调用系统,它的核心思想是远程服务在我本地执行,而不是通过通信传输执行结果。客户端本地有服务端提供的API的jar包,可以直接在本地执行远程服务的某个方法,具体执行哪个方法,可以直接写在java代码里,当然其底层的实现也是依赖通信的,核心是java的串行化。

二、?????WebService的实现标准有哪些?

Web service既然只是一种概念,那肯定有具体的实现标准,这些标准的核心就是要解决这样一个问题:以何种格式告诉服务端我要执行哪个类的哪个方法,以及给定的入参,以何种格式从服务端接收执行结果。

常见的标准有:PRC、SOAP、REST。RPC相对来说功能较简单,传递的数据类型也较少。SOAP具备比较完整的功能,但显得稍微有点重量级。REST的特点是简单高效,单规范性不如SOAP好。

  基于上面三种标准,分别有自己对应的实现,RPC有XML-RPC实现,SOAP有Apache SOAP、Axis、xfire,REST有restlet等。

三、?????基于Axis2实现的SOAP标准的WebService

1.?????概述

  一个WebService包括客户端和服务端,服务端通过具体的SOAP实现方案(如Axis2),将需要提供服务的类和方法发布成为services,并且生成对应的访问地址,客户端则通过RPC请求访问这个访问地址,并在请求中携带具体访问的方法名和参数,服务端在接收到这个客户端的请求后就执行业务逻辑,并返回运行结果。

2.?????基本术语

WebService三要素:SOAP、WSDL (Web Services Description Language)、UDDI(Universal Description Discovery and Integration )之一, soap用来传递信息的格式,WSDL用来描述如何访问具体的接口,uddi用来管理、分发、查询webService,具体实现可以搜索 Web Services简单实例

?---摘自百度百科

3.?????基于eclipse实现一个基本的Web Service/Client

本小节将介绍如何通过eclipse搭建一个web服务,实现客户端和服务端的通信

1)???????准备工作:

1. 下载Axis2包

从http://axis.apache.org/axis2/java/core/download.cgi下载,本文用的是1.6.2版本 Binary Distribution zip。

2. 配置eclipse

Eclipse本身就提供对webservice的支持,但由于存在版本差异,故需要更新eclipse,使其支持1.6.2版本,操作如下:Window --->Perferences--->Web service--->Axis2 Preferences 将最新下载的axis包路径配置到Axis2 Runtime location中。

?

2)???????第一步:创建一个普通的Web服务,并设计用于提供服务的接口

  在支持webservice之前,首先需要创建一个普通的web服务,并设计一些用于提供web服务的类和方法,然后将其发布到tomcat中并确保能成功运行。这样做的目的是,后面的步骤需要基于该web服务,增加webservice的特性,将我们设计用于提供web服务的类和方法通过webservice发布出去,所以首先要做的就是为POJO设计这些API,API要具备一些简单的业务逻辑,方便测试。此处选用一个service类作为POJO,其API是2个get方法

WebServiceProject就是一个普通的web工程,IssueSoapService是准备发布出去的服务类,其中的2个方法是发布的具体方法。这一步要确保整个服务能够在tomcat容器中成功启动和运行。

?

3)???????第二步:为创建的Web服务增加webservice的支持

  这一步是在上一步创建的普通的web工程的基础上,增加webservice的支持,这里我叫做"增加",但在eclipse中具体操作的时候,其实是通过新建来完成的,但效果还是"增加"的效果。增加的过程可以

  新建--->webservice--->在service implementation中指定需要发布的类,并选择 web service runtime:Apache Axis2--->然后next直到finish。  

单击Finish后,刚才的web工程,多了一些文件夹和文件,这些是由eclipse帮忙生成的Axis2支持webservice的文件,主要是webcontent下多了个axis2-web的文件夹,WEB-INF下多了modules和services文件夹。此时,如果不出错的话,那tomcat应该能成功启动该web服务

?

4)???????第三步:测试该服务

浏览器中输入如下地址:http://localhost:8080/WebServiceProject/, 页面显示axis2的首页并列出提供的web服务:点击Services,页面显示刚才配置的POJO及发布的方法,红色部分表示我们发布的类的服务信息,包括发布地址,发布接口列表点击打开IssueSoapService,页面显示wsdl描述文件:这个页面的url是这个soap接口的wsdl描述文件,targetNamespace表示的是命名空间,在客户

端访问是需要使用到,getIssue和getIssueByExt这是2个接口,wsdl文件描述了这个服务的所有信息。此时,可以通过在浏览器端输入如下地址来测试getIssue接口是否功能正常:http://localhost:8080/WebServiceProject/services/IssueSoapService/getIssue?issueKey=789根据getIssue的实现,要求给如一个入参issueKey,执行时会在控制台答应出issueKey的值,并返回该值。到这里,表示该webservice已经发布成功并可以被调用,下一步将介绍然后编写客户端

?

5)???????3.3.5 第四步:创建客户端

本步骤通过一个普通的java工程的main函数,发起对webservice的getIssue的调用。创建工程,并将axis2的jar包放入buildpath,然后在main函数中编写客户端访问服务端的代码,暂时使用同步访问,代码如下:

import javax.xml.namespace.QName;

import org.apache.axis2.AxisFault;

importorg.apache.axis2.addressing.EndpointReference;

import org.apache.axis2.client.Options;

import org.apache.axis2.rpc.client.RPCServiceClient;

public class ServiceClient {

?public static void main(String[] args) {

? //TODO Auto-generated method stub

?String target =

"http://localhost:8080/WebServiceProject/services/IssueSoapService";

? try{

??RPCServiceClient client = new RPCServiceClient();

??EndpointReference epr = new EndpointReference(target);

??Options options = client.getOptions();

??options.setTo(epr);

??QName qname = new QName("http://soap.xxx.com","getIssue");

??

?? //同步调用

??Object[] result = client.invokeBlocking(qname,new Object[]{"abc"},

new Class[] { String.class });

??System.out.println((String)result[0]);??

? }catch (AxisFault e) {

?? //TODO Auto-generated catch block

??e.printStackTrace();

? }

?}

}

6)???????第五步:客户端访问服务器

将该文件(或工程)以java application的形式运行,发起对服务端的访问,并且成功接收到返回值。

?

7)???????第六步:更新服务端提供的services

  如果IssueSoapService.java类需要再发布一个方法,或者说有一个新的类需要发布成webservice服务,则需要重新执行"第二步"。也就是说更新发布结果,也是通过第二步的new操作来实现的。

4.?????扩展Web Service/Client的功能

1)???????入参和返回值都使用自定义的POJO

1. 通常是服务端定义一个POJO,然后使用这个POJO作为返回值类型或者入参类型。同时,服务端将该POJO类(一系列POJO类)打成jar,供客户端使用

2. 客户端引入服务端提供的jar包,引入该POJO并使用其接收返回值

3. POJO定义时,必须实现串行化接口,服务端和客户端的POJO允许有不同的包路径

服务端:

issueBean.setNumber(number+" . Sessionnot exist,first access");

issueBean.setExternalKey(externalKey);

return issueBean;

?

客户端:

String target ="http://localhost:8080/WebServiceProject/services/IssueSoapService";

? try{

??RPCServiceClient client = new RPCServiceClient();

??EndpointReference epr = new EndpointReference(target);

??Options options = client.getOptions();

?? options.setTo(epr);

??QName qname = new QName("http://soap.com","getIssueBean");

??

?? //同步调用

??Object[] result = client.invokeBlocking(qname,new Object[]{"abc","externalkey"},new Class[] { IssueBean.class });

??IssueBean issueBean = (IssueBean)result[0];

??System.out.println("SyncServiceClient"+issueBean.getNumber()+" "+issueBean.getExternalKey());??

? }catch (AxisFault e) {

?? //TODO Auto-generated catch block

??e.printStackTrace();

? }

2)???????异步调用

Webservice通常情况下都是用同步调用的,此处只是介绍下异步调用

//异步访问

?? client.invokeNonBlocking(qname,new Object[] {"abc",new AxisCallback() {

???@Override

???public void onError(Exception ex) {

??? }

???@Override

???public void onComplete() {

??? }

???@Override

???public void onFault(MessageContext arg0) {

??? }

???@Override

???public void onMessage(MessageContext ctx) {

????// TODO Auto-generated method stub

????System.out.println("Message:" +ctx.getEnvelope().getFirstElement().getFirstElement().getFirstElement().getText());

??? }

??});

??System.out.println("AsyncServiceClient over!");

??

?? //阻止程序退出

??System.in.read();

注意,System.in.read();起到阻止程序退出的效果,一旦程序退出,那回调就不会起作用了的,所以异步其实意义不大。

3)???????手动发布和合并类

  手动发布(参考自网络):手动发布WebService,必须打包成.aar文件,.aar文件实际上就是改变了扩展名的.jar文件。在现在建立了两个文件:MyService.java和services.xml。将MyService.java编译,生成MyService.class。services.xml和MyService.class文件的位置如下:D:wsserviceMyService.class

D:wsMETA-INFservices.xml

??? 在windows控制台中进入ws目录,并输入如下的命令生成.aar文件(实际上,.jar文件也可以发布webservice,但axis2官方文档中建议使用.aar文件发布webservice):jar cvf ws.aar

最后将ws.aar文件复制到<Tomcat安装目>webappsaxis2WEB-INFservices目录中,启动Tomcat后,就可以调用这个WebService了

合并多个发布类,通过serviceGroup标签搞定,同时要求class文件要在一个目录下,或者打成同一个aar包:

   <serviceGroup>

   ??? <servicename="myService">

   ??????? <description>

   ??????????? Web Service例子

   ??????? </description>

   ??????? <parametername="ServiceClass">

   ???????????service.MyService?

   ??????? </parameter>

   ???????<messageReceivers>

   ???????????<messageReceiver mep=http://www.w3.org/2004/08/wsdl/in-out class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>

   ???????????<messageReceiver mep=http://www.w3.org/2004/08/wsdl/in-only class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver"/>

   ???????</messageReceivers>

   ??? </service>

<service name="myService1">

??????<description>

??????????Web Service例子

?????</description>

??????<parameter name="ServiceClass">

??????????service.MyService1

??????</parameter>

???????<messageReceivers>

?????????<messageReceivermep="http://www.w3.org/2004/08/wsdl/in-out"

??????????????class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>

?????????<messageReceivermep=http://www.w3.org/2004/08/wsdl/in-only class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver"/>

??????</messageReceivers>

??</service>

</serviceGroup>

?

? ? 手动增加一个service的方法(自己实践的):如果通过eclipse自动生成新的服务,曾通常是会在ebContentWEB-INFservices目录下新增一个以新服务类名为名称的文件夹,然后里面有两个文件夹,一个是class文件的包路径的第一层,另一个是META-INF。其实DtsSoapServeice和IssueSoapService这2个目录根本没用,它只eclipse在逻辑上帮我们划分的,而每个目录下面其实是对应服务的完整包路径和全部class文件,且都有独立的services.xml(只包含一个服务的service配置)。基于上面的发现,其实可以合并这些服务,合并后services/SoapService/com/xxx/soap/DtsSopaService.class、IssueSoapService.class:SoapService这个目录其实通常没有意义,只是逻辑上的划分,但是现在它已经不再和具体某个服务一一对应了。com.xxx.soap目录下具备2个服务的class文件。META-INF/Services.xml也做了合并,如下图:

<serviceGroup>

?<service name="IssueSoapService"scope="application" >

?<Description>

??Please Type your service description here

?</Description>

?<messageReceivers>

??<messageReceivermep="http://www.w3.org/2004/08/wsdl/in-only"class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver" />

??<messageReceiver?mep="http://www.w3.org/2004/08/wsdl/in-out"?class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>

?</messageReceivers>

?<parameter name="ServiceClass"locked="false">com.huawei.jira.soap.IssueSoapService</parameter>

?</service>

?<servicename="DtsSoapService" scope="application" >

?<Description>

??Please Type your service description here

?</Description>

?<messageReceivers>

??<messageReceivermep="http://www.w3.org/2004/08/wsdl/in-only"class="org.apache.axis2.rpc.receivers.RPCInOnlyMessageReceiver" />

??<messageReceiver?mep="http://www.w3.org/2004/08/wsdl/in-out"?class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>

?</messageReceivers>

?<parameter name="ServiceClass"locked="false">com.huawei.jira.soap.DtsSoapService</parameter>

?</service>

</serviceGroup>

这种合并有什么用呢?个人感觉使用场景就是把服务逻辑分组,一组服务内部可以共享session,具体如何共享会在3.4.5中讲到。

按照上面的说法,如果我要新增一个服务,是不是就可以手动将class及包路径拷贝到WebContentWEB-INFservices目录下,同时自己添加META-INF/services.xml文件即可,再也不用依赖eclipse自动生成了

?

4)???????3.4.4 webservice的session管理

Webservice为什么需要管理session?按理说,webservice只是提供无状态的服务,每次soap调用,都是彼此独立的,不需要记录任何状态,但是,对于同一客户端而言,有时候需要服务器记录它调用的一些结果,供下次调用时使用,最典型的就是第一次调用时需要鉴权(也就是登陆),一旦鉴权通过,随后的调用都不需要再鉴权,这就是记录状态,这种能力通常通过session来实现。因此,webservice需要提供对session的管理能力,从而能够提供有状态的web服务。

由于webservice本质上是通过servlet实现的,所以对状态的记录,其实可以通过容器提供的context来实现,事实上,webservice也的确是基于context实现的。补充知识:servlet本身其实是单例多线程的,即只有一个实例化对象,但是通过多线程来并发执行service方法,故servlet本身是无状态的,一个请求的状态,是由容器负责维护的(参见:http://www.cnblogs.com/yjhrem/articles/3160864.html)。

客户端代码:

try {

??RPCServiceClient client = new RPCServiceClient();

??EndpointReference epr = new EndpointReference(target);

??Options options = client.getOptions();

??options.setTo(epr);

??options.setManageSession(true);

??QName qname = new QName("http://soap.jira.huawei.com","getIssueBean");

??for(int i = 0; i < 3; i++)

?? {

???//同步调用

???Object[] result = client.invokeBlocking(qname,new Object[]{"abc"+i,"456"},new Class[] { IssueBean.class });

???IssueBean issueBean = (IssueBean)result[0];

???System.out.println("SyncServiceSessionClient 第 "+i+"次调用,返回值:number="+issueBean.getNumber()+",externalkey="+issueBean.getExternalKey());

?? }

? }

?

服务端代码:

public IssueBean getIssueBean(Stringnumber,String externalKey)

?{

?System.out.println("getIssueBean call,number = "+number);

?IssueBean issueBean = new IssueBean();

?MessageContext context = MessageContext.getCurrentMessageContext();

?ServiceGroupContext ctx = context.getServiceGroupContext();

?String numberValue = (String) ctx.getProperty("number");

? //第一次访问,session中为空,就返回

?if(StringUtils.isEmpty(numberValue))

? {

??issueBean.setNumber(number+" . Session not exist,firstaccess");

??issueBean.setExternalKey(externalKey);

??ctx.setProperty("number",number);

??System.out.println("已保存number:"+number);

? }

?else

? {

??issueBean.setNumber(number+" . Session exist,last number is:"+numberValue);

??issueBean.setExternalKey(externalKey);

? }

?return issueBean;

?}

?

从上面的例子可以得出如下几点:

1、 服务端通过MessageContext、ServiceContext、getProperty、setProperty共同完成session的更新和获取

2、 客户端需要打开session管理功能:options.setManageSession(true);

3、 客户端是通过java application运行的,无法保存sessionId,一旦运行结束再次启动时,就会让服务器重新创建session,故3次调用代码是在for循环中执行的。如果客户端是一个http请求,则可以通过cookie保存sessionId,可以用下面的地址进行测试,连续执行2次,结果不一样:http://localhost:8080/WebServiceProject/services/IssueSoapService/getIssueBean?number=789&externalKey=ddd

?

5)???????跨service的session管理

一个webservice可以有多个服务,3.4.4中讲到了如何确保一个服务在多次调用时,可以保存session,这节将会讲到,如何确保多个服务之间也能共享session。首先,需要共享session,就必须确保这些服务在同一分组中,也就是要把需要共享session的服务分到同一分组,通过services.xml的<serviceGroup>标签实现。这里把IssueSoapService和DtsSoapService这2个服务合并,并且把scop改为application即可。服务端读写session也有一些相应的变化,使用GroupContext:ServiceGroupContextctx = context.getServiceGroupContext();客户端不需要做任何改动,这样ctx中保存的属性,就可以被group中所有的服务共享。

?

6)???????3.4.6 Axis2与spring集成

四、?????基于Restlet实现的REST标准的WebService

1.?????4.1

五、?????Q&A

1.?????Axis2不会生出wsdl文件,那如何指定哪些类或者函数是被发布的?目前都是通过eclipse增加,实际是产生什么效果?

答:webservice自动从WEB-INFservices目录下寻找需要发布的类,通过eclipse发布的类,会在该目录下生出这个类对应的com和META-INF文件,META-INF中会有services.xml用来指明该类的发布属性,同时系统也会读取services.list中指明的aar包作为发布的类。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读