目录
前言
webService是什么
webService平台技术
XML+XSD-Extensible Markup Language(扩展性标记语言)
SOAP-Simple Object Access Protocol(简单对象访问协议)
WSDL-WebService Description Language(web服务描述语言)
webService调用过程
webService快速入门
自定义一个服务端
自定义一个客户端
wsimport命令使用
解析WSDL文件
基于Axis1.4调用正式webServ服务
总结
对于很多写java不久的人可能都没听过webService,或者听过对它也没有什么了解,毕竟RESTful技术太火了,新的项目基本都是基于RESTful开发,除非你的项目有接触到对接老系统之类的,最近在写脚本时正好有对接webService的需求,特地做个简单总结
又名web服务,简单来说webService就是一种跨编程语言和跨操作系统平台的远程调用技术,既然是远程调用则必定涉及到客户端和服务端,如果有接触过其他远程调用技术,其实这玩意也很好理解,无非就是服务端发布一个服务,客户端通过某种方式成功调用该服务完成业务需求,而webService则是基于HTTP协议传输数据,与RESTful请求大致上来说是相似的,只是前者是以xml形式传输数据,而后者更多是以json数据进行传输
XML+XSD,SOAP和WSDL便是webService的三大主要技术
使用XML的优点便是其本身就是与平台无关,与厂商无关,用它进行封装数据时不管服务端与客户端是否使用了相同的编程语言,整个调用过程丝毫不受影响,而XML Schema(XSD)定义了一整套标准的数据类型,并给出了一种语言来扩展这套数据类型,因此在封装数据时最好都会被转换为XSD类型,当然你不会感知到这一过程,你所使用的框架或工具都自动帮你完成了这个转换。可以说XML+XSD技术是整个web服务的基础
SOAP=HTTP协议+XML数据格式,可以看出SOAP协议是基于HTTP协议,同时也是基于XML语言的,这里需要注意的是webService只采用HTTP POST的方式传输数据,不使用GET方式,可以理解webService就是在HTTP的基础上发送SOAP协议,当Content-Type为text/xml时,表明这是基于SOAP1.1协议;当Content-Type为application/soap+xml时,表明这是基于SOAP1.2协议
WSDL是一个基于XML的语言,用于描述webService服务端的函数、参数和返回值,它是webService服务端和客户端都能理解的标准格式,其通过XML形式说明服务在什么地方,服务提供了什么样的方法。就好像我们去商店买东西,首先得知道商店里有什么,而商家的做法一般是张贴海报告诉客户,WSDL就类似于海报的作用,webService的客户端想要调用某个webService服务,首先也要知道这个服务的地址在哪,以及这个服务中有什么方法可以调用以及这些方法的参数和返回值是怎么样的
对客户端而言,我们给这类webService客户端API传递WSDL文件的url地址,这些API就会创建出底层的代理类,就可以访问到webService服务,代理类把客户端的方法调用转换成SOAP格式的请求数据再通过HTTP协议发出去,并把接收到的SOAP数据转换成返回值返回,服务端同理。
为了方便,我们先使用idea来创建一个webService工程,如下图,当然这种方式也很没有必要,会下载一堆东西,可直接创建Maven工程来使用
如果你采用上面的方式生成项目的话,此时项目结构如下,Maven工程的话可自行新建类
此时可直接启动main方法来生成一个webService服务端,启动后直接访问:http://localhost:9000/HelloWorld?wsdl
你会看到该服务所提供出去的WSDL文件,客户端就可以根据该文件来发起调用啦,下面我们回到服务端代码,来解释下部分注解及其作用,均放在代码中,如下:
@WebService(serviceName = "HelloManager", // 修改服务名,默认是当前类名后加上Service
targetNamespace = "http://example.xj.test") // 修改命名空间,默认包名,取反
public class HelloWorld {
/**
* @WebMethod(operationName=""): 修改方法名
* @WebResult(name=""):修改返回参数名
* @WebParam(name=""):修改输入参数名
* @param from
* @return
*/
@WebMethod(action = "http://example.xj.test/sayHelloWorldFrom") // 指定方法action
public String sayHelloWorldFrom(@WebParam(name = "from") String from) {
String result = "Hello, world, from " + from;
System.out.println(result);
return result;
}
@WebMethod(exclude = true) // 把该方法排除在外
public String excludMethod() {
return null;
}
public static void main(String[] argv) {
Object implementor = new HelloWorld ();
String address = "http://localhost:9000/HelloWorld";
Endpoint.publish(address, implementor);
System.out.println("Web service run success");
}
}
最后再来总结下整体步骤:
这里的客户端是基于Axis1.4开发的,即动态调用webService服务,测试代码如下:
public class AxisClient {
public static void main(String[] args) {
Service service = new Service();
try {
Call call = (Call) service.createCall();
String url = "http://localhost:9000/HelloWorld?wsdl";
// 设置服务端地址
call.setTargetEndpointAddress(new URL(url));
// 设置要执行的方法
call.setOperationName(new QName("http://example.xj.test", "sayHelloWorldFrom"));
// 如果WSDL的方法上有要求必须要填SOAPAction,则必须加上如下这一行,否则会报服务器无法识别HTTP头SOAPAction的值
// call.setSOAPActionURI("http://example.xj.test/sayHelloWorldFrom");
// 设置要传入的参数,如果没有要传入的参数则无需该行
call.addParameter("from", XMLType.XSD_STRING, ParameterMode.IN);
// 设置返回类型
call.setReturnType(XMLType.XSD_STRING);
// 设置header信息
SOAPHeaderElement element = new SOAPHeaderElement(new QName("Element"));
MessageElement user = new MessageElement(new QName("Username"), "admin");
MessageElement password = new MessageElement(new QName("Password"), "admin123");
element.addChild(user);
element.addChild(password);
call.addHeader(element);
// 调用webservice服务
String from = "webService客户端";
String result = (String) call.invoke(new Object[] {from});
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
调用代码很简单,注释也都很清楚,唯一需要注意的点就是在传参的地方,如果服务端的方法上未使用@WebParam注解指明参数名称,那么此处只能使用arg0,arg1以此类推,否则请使用特定的参数名,若传参名称不当服务端获取的总是为null
wsimport是jdk自带的一个命令,该命令可解析WSDL文件然后生成本地代理,当然本地代理也仅仅是拥有其方法和类,并不能解析出具体的实现,但对于我们客户端来说已经绰绰有余了,有了这些类我们在项目里便可以直接调用这些类来完成业务需求,本地代理会自行访问webService,无需我们自行实现客户端调用代码。当然在windows下使用该命令必须配置环境变量,且jdk版本最好是1.7以上,在mac下无需配置可直接使用。
wsimport常用参数为:
下面我们直接使用该命令来小试牛刀,使用的WSDL文件来源某个在线web服务网:http://www.webxml.com.cn/zh_cn/index.aspx
wsimport -s ./ http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx\?wsdl
./表示生成文件到当前目录,如果使用了-s参数则会在目录下生成两份代码,一份为.class代码,一份为.java代码。.class代码,可以经过打包以后使用。.java代码可以直接Copy到我们的项目中运行。
执行命令后我们可以看到:
这里的三个警告表示这三种调用方式都无法支持,只支持第一种调用方式即SOAP,具体调用方式可通过直接访问上面的WSDL文件查看,此时当前目录下已经存在解析后的java代码了,只需拷贝到我们的项目中即可进行调用
上面圈红的即wsimport解析后的java文件,除了MobileInfoTest,这是我写的测试类来进行调用本地代理类,如下:
public class MobileInfoTest {
public static void main(String[] args) {
// 生成服务对象
MobileCodeWS mobileCodeWS = new MobileCodeWS();
// 获得webService服务的访问方式
MobileCodeWSSoap mobileCodeWSSoap = mobileCodeWS.getMobileCodeWSSoap();
// 查询当前手机号归属地省份、地区和手机卡类型信息
String mobileInfo = mobileCodeWSSoap.getMobileCodeInfo("xxxx", null);
System.out.println(mobileInfo);
// 查询国内手机号归属地数据库信息,返回数据:一维字符串数组(省份 城市 记录数量)
ArrayOfString arrayOfString = mobileCodeWSSoap.getDatabaseInfo();
arrayOfString.getString().forEach(System.out::println);
}
}
执行后结果如下:
下面我们使用上面所写的Asis客户端代码来调用这个手机号归属地查询web服务,代码如下:
public static void main(String[] args) {
Service service = new Service();
try {
Call call = (Call) service.createCall();
String url = "http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?wsdl";
// 设置服务端地址
call.setTargetEndpointAddress(new URL(url));
// 设置要执行的方法
call.setOperationName(new QName("http://WebXml.com.cn/", "getMobileCodeInfo"));
// 如果WSDL的方法上有要求必须要填SOAPAction,则必须加上如下这一行,否则会报服务器无法识别HTTP头SOAPAction的值
call.setSOAPActionURI("http://WebXml.com.cn/getMobileCodeInfo");
// 设置要传入的参数,如果没有要传入的参数则无需该行
call.addParameter("mobileCode", XMLType.XSD_STRING, ParameterMode.IN);
call.addParameter("userID", XMLType.XSD_STRING, ParameterMode.IN);
// 设置返回类型
call.setReturnType(XMLType.XSD_STRING);
//call.setReturnQName(new QName("http://WebXml.com.cn/", "getMobileCodeInfoResult"));
// 调用webservice服务
String result = (String) call.invoke(new Object[] {"xxx", null});
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
运行后始终报错,如下,毕竟才接触webService不久,对WSDL文件也了解的不太够,不知道这种正式的Web服务到底该如何正确的传参,好像服务端始终没接收到这个手机号,不知道是不是序列化的问题,望看到这里的大佬如果测试出来后能解疑一下,感谢!
其实我还没真正去调用一个提供出来的webService服务,但是为了在脚本中支持调用才稍微了解了一下并提供了一个简单webSer工具方法,就是如上经过封装的客户端代码,我们只需记住webService只是在传输数据的时候使用了xml,其余方法调用和参数传递其实都类似。当然真正在项目中开发的话肯定是基于wsimport命令解析后的代码来进行二次开发即可。