一、什么是WebService(来源百度百科)
Web service是一个平台独立的,低耦合的,自包含的、基于可编程的web的应用程序,可使用开放的XML(标准通用标记语言下的一个子集)标准来描述、发布、发现、协调和配置这些应用程序,用于开发分布式的互操作的应用程序。
Web Service技术, 能使得运行在不同机器上的不同应用无须借助附加的、专门的第三方软件或硬件, 就可相互交换数据或集成。依据Web Service规范实施的应用之间, 无论它们所使用的语言、 平台或内部协议是什么, 都可以相互交换数据。Web Service是自描述、 自包含的可用网络模块, 可以执行具体的业务功能。Web Service也很容易部署, 因为它们基于一些常规的产业标准以及已有的一些技术,诸如标准通用标记语言下的子集XML、HTTP。Web Service减少了应用接口的花费。Web Service为整个企业甚至多个组织之间的业务流程的集成提供了一个通用机制。
二、发布
Web service 就是一个应用程序,它向外界暴露出一个能够通过Web进行调用的API。这就是说,你能够用编程的方法通过Web来调用这个应用程序。
我们将调用这个Web service 的应用程序称作客户端,运行这个Web service 的应用程序称作服务端。
在我之前创建的Web service项目其实就是一个服务端,这个HelloWorld类能实现的功能就是当客户端提交一个字符串的时候,返回一个"Hello, world, from " + 字符串;
public class HelloWorld { public String sayHelloWorldFrom(String from) { String result = "Hello, world, from " + from; System.out.println(result); return result; } }
那么我们就需要将这个类发布在Web上,在Web service项目的客户端中是在server-config.wsdd中编写XML进行发布
<service name="HelloWorld" provider="java:RPC" style="document" use="literal"> <parameter name="className" value="example.HelloWorld"/> <parameter name="allowedMethods" value="*"/> <parameter name="scope" value="Application"/> <namespace>http://example</namespace> </service>
这里每个标签的具体含义以及填写内容:
- name="HelloWorld"就是这个WebService的名字,可任意填写
- provider="java:RPC"指的是服务类型,可填写四种类型RPC、Document、Wrapped、Message
- <parameter name="className" value="example.HelloWorld"/>这个节点指出具体的类,value处填写类的路径
- <parameter name="allowedMethods" value="*"/>这个节点表示可以调用所有的public方法,也可以自己指定
- <parameter name="scope" value="Application"/>这个节点配置了service object,这里可以填写request、session、application
- requst :这个选项会让AXIS为每一个SOAP的请求产生一个服务对象,可以想像如果这个webservice的对象足够复杂,而且SOAP的请求过多,这个选项是非常耗费服务器性能的。
-
session:表示对同一个客户代理对象所发送的请求使用同一个服务对象,并把服务信息放在同一个上下文当中。
-
application: 类似于使用单体模式,表示所示的请求均使用同一个服务对象
然后点击运行。。。
再然后就是客户端的代码,新建一个ClientWSDD类
public class ClientWSDD { public static void main(String[] args){ try { String url = "http://localhost:8080/test/services/HelloWorld?wsdl"; //生成服务对象Service Service service = new Service(); Call call = (Call) service.createCall(); //设置Endpoint地址 call.setTargetEndpointAddress(new java.net.URL(url)); //绑定请求方法名称 call.setOperationName(new QName(url, "sayHelloWorldFrom")); //通过call.invoke 调用服务,获取返回值 String result = (String) call.invoke(new Object[]{"xw"}); System.out.println("result = " + result); }catch (ServiceException e){ System.out.println("ServiceException"); e.printStackTrace(); }catch (RemoteException e){ System.out.println("RemoteException"); e.printStackTrace(); } catch (MalformedURLException e) { System.out.println("MalformedURLException"); e.printStackTrace(); } } }
具体使用流程注释里写得很清楚,要注意的就是方法名,传入参数的个数,最后运行这个ClientWSDD类,可以看到输出:
输入的参数是xw,最终返回的结果是Hello, world, from xw。
三、高级特性Handler
Handler在功能上类似与Filter过滤器可以在WebService服务被调用前调用,也能在调用后调用。
这样我们可以实现WebService权限验证、记录调用次数,这次这里就实现记录某个WebService被调用次数的Handler
首先创建一个继承BasicHandler的类,BasicHandler是一个Axis的抽象类,然后实现其中的invoke()方法:
public class HelloWorldHandler extends BasicHandler { private static final long UID = 2333333L; private static long COUNT = 0L; private int requestCount = 0; @Override public void invoke(MessageContext messageContext) throws AxisFault { requestCount++; COUNT++; String status = (String) this.getOption("status"); System.out.println("HelloWorldHandler status " + status + ", COUNT " + COUNT + ", requestCount " + requestCount); } }
messageContext是一个Axis的上下文,里面存储了一些Axis和WebService的基本信息。这个类中有一个静态变量COUNT用于记录Handler的被调用次数,每次Handler被调用的时候都将加一
再然后我们还需要发布这个Handler,在server-config.wsdd中编写XML:
<handler name="HelloWorldHandler" type="java:example.HelloWorldHandler"> <parameter name="status" value="success"/> </handler> <service name="HelloWorldWSDDHandler" provider="java:RPC"> <requestFlow> <handler type="HelloWorldHandler"/> </requestFlow> <parameter name="className" value="example.HelloWorld"/> <parameter name="allowedMethods" value="*"/> <parameter name="scope" value="request"/> <responseFlow> <handler type="HelloWorldHandler"/> </responseFlow> </service>
- <handler></handler>中先是创建了一个名为HelloWorldHandler,类的路径是example.HelloWorldHandler的Handler,还创建了一个值为success的status参数
- 然后创建了一个service服务,名为HelloWorldWSDDHandler,服务类型为RPC,这个服务使用的类是example.HelloWorld
-
<requestFlow><handler type="HelloWorldHandler"/></requestFlow>表示在调用这个服务前将先调用这个HelloWorldHandler
-
<responseFlow><handler type="HelloWorldHandler"/></responseFlow>表示调用这个服务后将先调用这个HelloWorldHandler
服务端点击运行之后可以在http://localhost:8080/test/services页面中看到多了一个HelloWorldWSDDHandler服务:
客户端的调用和之前类似,只是需要修改URL,因为具体调用的功能和刚才一样,但是调用的服务不一样了
public class ClientWSDD {
public static void main(String[] args){
try {
String url =
"http://localhost:8080/test/services/HelloWorldWSDDHandler?wsdl";
//生成服务对象Service
Service service = new Service();
Call call = (Call) service.createCall();
//设置Endpoint地址
call.setTargetEndpointAddress(new java.net.URL(url));
//绑定请求方法名称
call.setOperationName(new QName(url, "sayHelloWorldFrom"));
//通过call.invoke 调用服务,获取返回值
String result = (String) call.invoke(new Object[]{"xw"});
System.out.println("result = " + result);
}catch (ServiceException e){
System.out.println("ServiceException");
e.printStackTrace();
}catch (RemoteException e){
System.out.println("RemoteException");
e.printStackTrace();
} catch (MalformedURLException e) {
System.out.println("MalformedURLException");
e.printStackTrace();
}
}
}
在点击运行,在客户端的输出和刚才一样,但是在服务端可以看到输出:
这个HelloWorldHandler在服务被调用前后被调用,如果只在服务调用前或者之后调用那么就能实现记录被调用次数的功能。
同一个Handler类可以当作两个不一样的Handler调用,这只需要在发布的时候再发布一个Handler就行:
<handler name="HelloWorldHandler" type="java:example.HelloWorldHandler"> <parameter name="status" value="success"/> </handler> <handler name="HelloWorldHandler2" type="java:example.HelloWorldHandler"> <parameter name="status" value="success"/> </handler> <service name="HelloWorldWSDDHandler" provider="java:RPC"> <requestFlow> <handler type="HelloWorldHandler"/> </requestFlow> <parameter name="className" value="example.HelloWorld"/> <parameter name="allowedMethods" value="*"/> <parameter name="scope" value="request"/> <responseFlow> <handler type="HelloWorldHandler2"/> </responseFlow> </service>
这里我又发布了一个名为HelloWorldHandler2的Handler,然后HelloWorldWSDDHandler之前将调用HelloWorldHandler,之后将调用HelloWorldHandler2。然后在运行两次客户端代码,最后的输出是:
因为COUNT是静态变量所以调用了两次就被加一两次,而request则没有,所以可以看出这里创建了两个HelloWorldHandler对象。这里的requset完成了记录WebService被调用次数的功能
四、高级特性Chain
Chain用于实现一连串的Handler功能,因为是一连串的所以。。需要先再创建一个Handler类,这里修改了UID和输出:
public class HelloWorldHandler2 extends BasicHandler { private static final long UID = 3222222L; private static long COUNT = 0L; private int requestCount = 0; @Override public void invoke(MessageContext messageContext) throws AxisFault { requestCount++; COUNT++; String status = (String) this.getOption("status"); System.out.println("HelloWorldHandler2 status " + status + ", COUNT " + COUNT + ", requestCount " + requestCount); } }
然后是创建Chain类,在Chain的构造函数中,需要创建想要调用的Handler类对象实例,然后使用addHandler方法将两个Handler加载进行,
public class HelloWorldChain extends SimpleChain { private static final long UID = 2222333L; public HelloWorldChain(){ HelloWorldHandler handler1 = new HelloWorldHandler(); HelloWorldHandler2 handler2 = new HelloWorldHandler2(); this.addHandler(handler1); this.addHandler(handler2); } }
然后是发布的xml:
<handler name="HelloWorldChain" type="java:example.HelloWorldChain"> </handler> <service name="HelloWorldWSDDChain" provider="java:RPC"> <requestFlow> <handler type="HelloWorldChain"/> </requestFlow> <parameter name="className" value="example.HelloWorld"/> <parameter name="allowedMethods" value="*"/> <parameter name="scope" value="request"/> <responseFlow> <handler type="HelloWorldChain"/> </responseFlow> </service>
和之前发布Handler类似的使用方法,我看资料用的标签是<chain></chain>但是不知道为什么我这里会报错,所以我直接修改成了Handler也能成功运行,所以其实chain也是Handler。
运行之后同样先在浏览器中的http://localhost:8080/test/services页面查看服务是否正常发布,
然后修改客户端的代码中的URL,
public class ClientWSDD { public static void main(String[] args){ try { String url = "http://localhost:8080/test/services/HelloWorldWSDDChain?wsdl"; //生成服务对象Service Service service = new Service(); Call call = (Call) service.createCall(); //设置Endpoint地址 call.setTargetEndpointAddress(new java.net.URL(url)); //绑定请求方法名称 call.setOperationName(new QName(url, "sayHelloWorldFrom")); //通过call.invoke 调用服务,获取返回值 String result = (String) call.invoke(new Object[]{"xw"}); System.out.println("result = " + result); }catch (ServiceException e){ System.out.println("ServiceException"); e.printStackTrace(); }catch (RemoteException e){ System.out.println("RemoteException"); e.printStackTrace(); } catch (MalformedURLException e) { System.out.println("MalformedURLException"); e.printStackTrace(); } } }
然后是服务端的输出:
可以看到HelloWorldHandler2将在HelloWorldHandler之后被调用。