Table of Contents
一、webservice定义
就是基于Web的服务。它使用Web(HTTP)方式,接收和响应外部系统的某种请求。从而实现远程调用.
名词1:XML. Extensible Markup Language -扩展性标记语言 XML,用于传输格式化的数据,是Web服务的基础。 namespace-命名空间。 xmlns=“http://itcast.cn” 使用默认命名空间。 xmlns:itcast=“http://itcast.cn”使用指定名称的命名空间。
名词2:WSDL – WebService Description Language – Web服务描述语言。 通过XML形式说明服务在什么地方-地址。 通过XML形式说明服务提供什么样的方法 – 如何调用。
名词3:SOAP-Simple Object Access Protocol(简单对象访问协议) SOAP作为一个基于XML语言的协议用于有网上传输数据。 SOAP = 在HTTP的基础上+XML数据。 SOAP是基于HTTP的。 SOAP的组成如下: Envelope – 必须的部分。以XML的根元素出现。 Headers – 可选的。 Body – 必须的。在body部分,包含要执行的服务器的方法。和发送到服务器的数据。
WebService的特点 WebService通过HTTP POST方式接受客户的请求 WebService与客户端之间一般使用SOAP协议传输XML数据. 它本身就是为了跨平台或跨语言而设计的。
WebService的注解包括:
@WebService-定义服务 --类上
@WebMethod-定义方法 - 方法
@WebResult-定义返回值 – 返回值
@WebParam-定义参数 – 参数
*************************************************************************************************************************************************
二、wsdl 解析
targetNamespace:相当于是java中的包。有些可以有import就是因为这个不痛
xmlns:命名空间
types元素:该元素就是标准的schema文档,一个方法有2个message:一个请求一个返回
portType :包含wsdl:operation 的就是方法
binding :包含了operation子元素。
service :service包含port子元素,绑定的地址和服务名
简单理解:
service:类
binding:接口
portType:方法
message:参数 fault是属于抛出的异常
wsdl:说明服务在哪里,如何调用,其实就是一个使用说明书:
理解WSDL文档的意义
soap:请求数据
soap响应数据
*************************************************************************************************************************************************
三、webservice服务端发布
***添加拦截器
// Endpoint.publish("http://192.168.1.113:6789/hello",new HelloServer());
EndpointImpl publish = (EndpointImpl) Endpoint.publish("http://192.168.71.1:6789/hello", new HelloServer());
publish.getOutInterceptors().add(new LoggingOutInterceptor(new PrintWriter(new FileWriter("out.txt"))));
publish.getInInterceptors().add(new LoggingInInterceptor(new PrintWriter(new FileWriter("in.txt"))));
System.out.println("添加拦截器成功");
1 通过jdk自带的发布
在类上添加@WebService注解。
这是jdk1.6提供的一个注解。它位于:javax.jws.*包中。
@BindingType(value = SOAPBinding.SOAP12HTTP_BINDING) jdk默认支持soap1.1,加上注解可以支持soap1.2
通过EndPoint(端点服务)发布一个webService。
Endpoint也是jdk提供的一个专门用于发布服务的类,它的publish方法接收两个参数,一个是本地的服务地址,二是提供服务的类。它位于javax.xml.ws.*包中。 static Endpoint.publish(String address, Object implementor) 在给定地址处针对指定的实现者对象创建并发布端点。
stop方法用于停止服务。
EndPoint发布完成服务以后,将会独立的线程运行。所以,publish之后的代码,可以正常执行。
其他注意事项: 给类添加上@WebService注解后,类中所有的非静态方法都将会对外公布。 不支持静态方法,final方法。
如果希望某个方法(非static,非final)不对外公开,可以在方法上添加@WebMethod(exclude=true),阻止对外公开。 如果一个类 上,被添加了@WebService注解,则必须此类至少有一个可以公开的方法,否则将会启动失败。
WebService只采用HTTP POST方式传输数据,不使用GET方式; -- 握手,WSDL-get,
普通http post的contentType为 application/x-www-form-urlencoded
WebService的contentType为-即在Http的基础上发SOAP协议 text/xml 这是基于soap1.1协议。 application/soap+xml 这是基于soap1.2协议。
SOAP1.1和SOAP1.2的 namespace不一样。可以通过查看类 javax.xml.ws.soap.SOAPBinding来查看里面的常量 默认情况下,Jdk1.6只支持soap1.1 即:@BindingType(value=javax.xml.ws.soap.SOAPBinding.SOAP11HTTP_BINDING)
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.ws.Endpoint;
/**
使用JDK发布方式
* @version:
*/
@WebService(serviceName = "HelloServer",targetNamespace = "http://server.yubin.com.cn/")
public class HelloServer {
@WebMethod(operationName = "sayHello")
@WebResult(name = "myreturn")
public String sayHello(@WebParam(name = "argNum1") String name) {
System.out.println("helloServer...sayHello()");
return "hello:"+name;
}
public String sayHello2(String name) {
System.out.println("helloServer...sayHello2()");
return "hello2:"+name;
}
public static void main(String[] args) {
System.out.println("发布服务:http://192.168.1.113:6789/hello.wsdl");
// Endpoint.publish("http://192.168.1.113:6789/hello",new HelloServer());
EndpointImpl publish = (EndpointImpl) Endpoint.publish("http://192.168.71.1:6789/hello", new HelloServer());
publish.getOutInterceptors().add(new LoggingOutInterceptor(new PrintWriter(new FileWriter("out.txt"))));
publish.getInInterceptors().add(new LoggingInInterceptor(new PrintWriter(new FileWriter("in.txt"))));
System.out.println("添加拦截器成功");
}
}
2 通过工具cxf 发布
用两个不同的类发布应用:
ServerFactoryBean --FacotryBean
JaxWsServerFactoryBean(建议使用此类)
使用ServerFactoryBean发布服务。 使用CXF发布一个服务,与JDK6发布一个服务完全不同 * 即使是不使用@WebService注解,一样可以发布成功 * 即使此类没有对外公布的方法一样可以发布成功
JaxWsServerFactoryBean是ServerFactoryBean的子类,也是功能扩展类。 但在CXF的API文档中没有提供此类API,请通过查看源代码的方式获取此类的帮助。 此类,必须要在被发布为服务的类上添加@WebService注解,如果不加注解,虽然不 出错,但也不会对外暴露任何方法。 使用此类生成的wsdl文件更加规范。 以下是从它的源代码中找到的对此类的说明。
使用ServerFactoryBean发布服务以后,在没有接口的情况下,可以使用wsimport生成的客户端代码调用成功。 但如果要使用ClientProxyFactoryBean客户端去调用服务器,则必须要先在服务器端创建一个接口。(一直以来,Spring都要求面向接口编程,而cxf与Spring能很好的整合,也就在此。),所以,必须要重写服务器端的代码。这将导致刚才使用wsimport生成的调用代码失效。 同时, ClientProxyFactoryBean由于要用到CXF环境,所以要导入CXF的所有包。 同时,如果是在本项目中调用,则可以直接使用本项目的接口。 如果在其他项目中调用,仍然需wsimport生成的类,但只需要一个接口。
1 ServerFactoryBean 发布服务
public interface IHelloServerCXF {
public String sayHello(String name);
}
/**
ServerFactoryBean
使用ServerFactoryBean发布CXF的Web服务
规范的做法应该是先书写一个接口
bean.setServiceBean(new HiService()); 这里提供服务的类型如果是接口@WebService那么注解就应该用在接口上
*/
public class HelloServerCXF implements IHelloServerCXF {
public String sayHello(String name){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(new Date()) +"sayHello()... name:" + name);
return sdf.format(new Date()) + " hello " + name;
}
//使用ServerFactoryBean发布CXF的Web服务
public static void main(String[] args) {
ServerFactoryBean bean = new ServerFactoryBean();
bean.getInInterceptors().add(new LoggingInInterceptor());
bean.getOutInterceptors().add(new LoggingOutInterceptor());
//服务的发布地址
// bean.setAddress("http://192.168.1.113:6789/hello");
bean.setAddress("http://192.168.0.213:6789/hello");
//提供服务的类的类型
bean.setServiceClass(IHelloServerCXF.class);
//提供服务的实例
bean.setServiceBean(new HelloServerCXF());
//发布服务 publish()...
bean.create();
System.out.println("server ready...");
}
}
调用ClientProxyFactoryBean
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
2 JaxWsServerFactoryBean 发布
@WebService
public interface IHelloServerCXFJaxWs {
public String sayHi(String name);
}
/**
** 使用JaxWsServerFactoryBean发布CXF的Web服务
* * 必须加入WebService注解,如果不加,虽然不报错,但是所有的方法都暴露不出来
*/
public class HelloServerCXFJaxWS implements IHelloServerCXFJaxWs{
public String sayHi(String name){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("sayHi()... name:" + name);
return sdf.format(new Date()) + " hi " + name;
}
public static void main(String[] args) {
JaxWsServerFactoryBean bean = new JaxWsServerFactoryBean();
//服务的地址
bean.setAddress("http://192.168.1.113:6786/hi");
//提供服务的类的类型
bean.setServiceClass(HelloServerCXFJaxWS.class);
//提供服务的实例
bean.setServiceBean(new HelloServerCXFJaxWS());
//发布服务 publish()...
bean.create();
System.out.println("server ready...");
}
}
调用:JaxWsProxyFactoryBean
*************************************************************************************************************************************************
四、webservice 客户端调用
1 wsimport 调用
调用的就是看wsdl描述文档:wsdl:service name="HelloServer" ,
就需要去找客户端生成的代码中哪个是继承server类的,然后new出来,调用其中的getxxxPort方法返回一个接口类( name="HelloServer")
//客户端添加拦截器
Client client = ClientProxy.getClient(port);
client.getOutInterceptors().add(new LoggingOutInterceptor());
client.getInInterceptors().add(new LoggingInInterceptor());
/**
*调用的就是看wsdl描述文档:wsdl:service name="HelloServer" ,
*就需要去找客户端生成的代码中哪个是继承server类的,然后new出来,调用其中的getxxxPort方法**返回一个接口类( name="HelloServer")
*/
public class Application_Client {
public static void main(String[] args) {
HelloServerService helloServerService = new HelloServerService();
HelloServer port = helloServerService.getHelloServerPort();
//客户端添加拦截器
Client client = ClientProxy.getClient(port);
client.getOutInterceptors().add(new LoggingOutInterceptor());
client.getInInterceptors().add(new LoggingInInterceptor());
String s = port.sayHello("张三");
System.out.println(s);
}
}
2 URLConnection
public static void main(String[] args) throws Exception {
//获取服务地址
URL url = new URL("http://192.168.1.113:6789/hello");
//
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type","text/xml;charset=UTF-8");
connection.setRequestMethod("POST");
OutputStream stream = connection.getOutputStream();
String soap ="<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:ser=\"http://server.yubin.com/\">"+
"<soapenv:Header/><soapenv:Body> <ser:sayHello> <arg0>aaa</arg0></ser:sayHello></soapenv:Body></soapenv:Envelope>";
stream.write(soap.getBytes());
InputStream inputStream = connection.getInputStream();
byte[] b = new byte[1024];
int len = 0;
String s = "";
while((len = inputStream.read(b)) != -1) {
String ss = new String(b,0,len,"UTF-8");
s += ss;
}
System.out.println(s);
inputStream.close();
stream.close();
connection.disconnect();
}
3 client 调用
public static void main(String[] args) throws Exception {
URL url = new URL("http://192.168.1.113:6789/hello?wsdl");
Service s = Service.create(url,new QName("http://server.yubin.com/","HelloServerService"));
HelloServer server = s.getPort(new QName("http://server.yubin.com/", "HelloServerPort"), HelloServer.class);
String res = server.sayHello("ceshi");
System.out.println(res);
}
4 ajax 调用
var xhr = null;
if(window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
}else {
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
function sendMsg(){
var name = document.getElementById('name').value;
var wsUrl = 'http://192.168.1.113:6789/hello';
var soap = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://server.yubin.com/">' +
'<soapenv:Header/><soapenv:Body> <ser:sayHello> <arg0>'+name+'</arg0></ser:sayHello></soapenv:Body></soapenv:Envelope>';
xhr.open('POST',wsUrl,true);
xhr.setRequestHeader("Content-Type","text/xml;charset=UTF-8");
xhr.onreadystatechange = _back;
xhr.send(soap);
}
function _back(){
if(xhr.readyState == 4){
if(xhr.status == 200){
var ret = xhr.responseXML;
var msg = ret.getElementsByTagName('return')[0];
document.getElementById('showInfo').innerHTML = msg.text;
}
}
}
五、wsimport 和cxf生成客户端代码
方式一:wsimport
获取webservice的wsdl文件
输入命令:wsimport unifiedOrder.wsdl -keep -p wy.soap.order -s d:/mytest就可以生成代码到mytest目录下
-keep keepgenerated files
-p 生成的包名
-s 指定产生代码所在的目录
或者直接从url生成
wsimport -d generated http://esample.org/sock?wskl
-b: 指定绑定文件或者附加模式
-B: 将此选项传递给jaxb模式编辑器
-catalog: 指定用于解析外部实体引用的目录文件
-encoding 指定源文件所使用的字符编码
-extension 允许供应商扩展,不按照规范指定功能,使用扩展可能会导致程序不可移植或无法实现其他进行互相
-help 显示帮助
-keep 保留生成的文件
-p 指定目标程序包
-quiet 隐藏wsimport输出
-httpproxy:<host>:<port> 指定http代码服务器,端口默认8080
***********************************
用法: wsimport [options] <WSDL_URI>
\其中 [options] 包括:
-b <path> 指定 jaxws/jaxb 绑定文件或附加模式
(每个 <path> 都必须具有自己的 -b)
-B<jaxbOption> 将此选项传递给 JAXB 模式编译器
-catalog <file> 指定用于解析外部实体引用的目录文件
支持 TR9401, XCatalog 和 OASIS XML 目录格式。
-d <directory> 指定放置生成的输出文件的位置 只有class文件
-encoding <encoding> 指定源文件所使用的字符编码
-extension 允许供应商扩展 - 不按规范
指定功能。使用扩展可能会
导致应用程序不可移植或
无法与其他实现进行互操作
-help 显示帮助
-httpproxy:<host>:<port> 指定 HTTP 代理服务器 (端口默认为 8080)
-keep 保留生成的文件
-p <pkg> 指定目标程序包
-quiet 隐藏 wsimport 输出
-s <directory> 指定放置生成的源文件的位置
-target <version> 按给定的 JAXWS 规范版本生成代码
默认为 2.2, 接受的值为 2.0, 2.1 和 2.2
例如, 2.0 将为 JAXWS 2.0 规范生成兼容的代码
-verbose 有关编译器在执行什么操作的输出消息
-version 输出版本信息
-wsdllocation <location> @WebServiceClient.wsdlLocation 值
-clientjar <jarfile> 创建生成的 Artifact 的 jar 文件以及
调用 Web 服务所需的 WSDL 元数据。
-generateJWS 生成存根 JWS 实现文件
-implDestDir <directory> 指定生成 JWS 实现文件的位置
-implServiceName <name> 生成的 JWS 实现的服务名的本地部分
-implPortName <name> 生成的 JWS 实现的端口名的本地部分
\扩展:
-XadditionalHeaders 映射标头不绑定到请求或响应消息不绑定到
Java 方法参数
-Xauthfile 用于传送以下格式的授权信息的文件:
http://username:password@example.org/stock?wsdl
-Xdebug 输出调试信息
-Xno-addressing-databinding 允许 W3C EndpointReferenceType 到 Java 的绑定
-Xnocompile 不编译生成的 Java 文件
-XdisableAuthenticator 禁用由 JAX-WS RI 使用的验证程序,
将忽略 -Xauthfile 选项 (如果设置)
-XdisableSSLHostnameVerification 在提取 wsdl 时禁用 SSL 主机名
验证
\示例:
wsimport stock.wsdl -b stock.xml -b stock.xjb
wsimport -d generated http://example.org/stock?wsdl
方式二: cxf
生成客户端程序。利用cxf插件直接生成wsdl2java命令
wsdl2java -p com -d src -encoding utf-8 -all wsdl
-p:指定wsdl命名空间,也就是要生成的包名
-d:指定要产生代码所在的目录
-client:生成客户端测试web service代码
-server:生成服务器启动web service代码
-impl:生成web service的实现代码
-ant: 生成build.xml文件
-all: 生成所有有开始端点代码
-b: 指定绑定文件或者附加模式