什么是WebServices?
它是一种构建应用程序的普遍模型,可以在任何支持网络通信的操作系统中实施运行;它是一种新的web应用程序分支,是自包含、自描述、模块化的应用,可以发布、定位、通过web调用。Web Service是一个应用组件,它逻辑性的为其他应用程序提供数据与服务.各应用程序通过网络协议和规定的一些标准数据格式(Http,XML,Soap)来访问Web Service,通过Web Service内部执行得到所需结果.Web Service可以执行从简单的请求到复杂商务处理的任何功能。一旦部署以后,其他Web Service应用程序可以发现并调用它部署的服务。
关键的技术和规则
在构建和使用Web Service时,主要用到以下几个关键的技术和规则:
1.XML:描述数据的标准方法.
2.SOAP:表示信息交换的协议.
3.WSDL:Web服务描述语言.
4.UDDI:通用描述、发现与集成,它是一种独立于平台的,基于XML语言的用于在互联网上描述商务的协议。
XML
可扩展的标记语言(XML)是Web service平台中表示数据的基本格式。除了易于建立和易于分析外,XML主要的优点在于它既是平台无关的,又是厂商无关的。无关性是比技术优越性更重要的:软件厂商是不会选择一个由竞争对手所发明的技术的。
SOAP
SOAP是web service的标准通信协议,SOAP为simple object access protocoll的缩写,简单对象访问协议. 它是一种标准化的传输消息的XML消息格式。
WSDL
WSDL的全称是web service Description Language,是一种基于XML格式的关于web服务的描述语言。其主要目的在于web service的提供者将自己的web服务的所有相关内容,如所提供的服务的传输方式,服务方法接口,接口参数,服务路径等,生成相应的完全文档,发布给使用者。使用者可以通过这个WSDL文档,创建相应的SOAP请求消息,通过HTTP传递给webservice提供者;web服务在完成服务请求后,将SOAP返回消息传回请求者,服务请求者再根据WSDL文档将SOAP返回消息解析成自己能够理解的内容。
UDDI
将web service进行UDDI注册发布,UDDI是一种创建注册表服务的规范,以便大家将自己的web service进行注册发布供使用者查找.然而当服务提供者想将自己的web service向全世界公布,以便外部找到其服务时,那么服务提供者可以将自己的web service注册到相应的UDDI商用注册网站,目前全球有IBM等4家UDDI商用注册网站。因为WSDL文件中已经给定了web service的地址URI,外部可以直接通过WSDL提供的URI进行相应的web service调用。所以UDDI并不是一个必需的web service组件,服务方完全可以不进行UDDI的注册。
签于webservice开发C/S或B/S结构中的重要性,一直在研究其原理。本章主要讲述使用JDK自带的javax扩展包创建webservice服务的过程。我们创建通用性高的web服务接口时一般采取java基础类型来作为参数传递和返回,比如string,byte[],int等。string多采用XML格式来进行规范化交互,byte[]常用来进行二进制的处理,这样使用web service可以做很多设计了。
我们提供一个范式的服务器端代码:
import
javax.annotation.Resource;
import
javax.jws.WebMethod;
import
javax.jws.WebParam;
import
javax.jws.WebResult;
import
javax.jws.WebService;
import
javax.jws.soap.SOAPBinding;
import
javax.xml.ws.WebServiceContext;
import
com.sun.net.httpserver.HttpExchange;
import
com.ue.dto.CrackBean;
import
com.ue.server.CrackControl;
import
com.ue.util.ConvertUtils;
@WebService
(name =
"client"
, targetNamespace =
"http://ws.ue.com"
, serviceName =
"client"
)
@SOAPBinding
(style =
SOAPBinding
.Style.
RPC
)
public
class
CrackClientImp
implements
CrackClient
{
@Resource
private
WebServiceContext
wsContext
;
private
String getIp()
{
HttpExchange exchange = (HttpExchange)
wsContext
.getMessageContext().get(
"com.sun.xml.internal.ws.http.exchange"
);
String remortAddress = exchange.getRemoteAddress().getAddress().getHostAddress();
return
remortAddress;
}
// 获得破解开
@WebMethod
(operationName =
"getCrack"
, action =
"getCrack"
, exclude =
false
)
@WebResult
(name =
"returnWord"
)
public
byte
[] getCrack ()
{
byte
[] defaultvalue =
""
.getBytes();
byte
[] code =
null
;
CrackControl c = CrackControl. getInstance();
CrackBean bean = c.take();
if
(bean !=
null
)
{
byte
[] name = bean.getTypeName().getBytes();
byte
[] pack = ConvertUtils.intToBytes4(name.
length
);
byte
[] content = bean.getContent();
code =
new
byte
[content.
length
+ pack.
length
+ name.
length
];
System. arraycopy(pack, 0, code, 0, pack.
length
);
System. arraycopy(name, 0, code, pack.
length
, name.
length
);
System. arraycopy(content, 0, code, pack.
length
+ name.
length
, content.
length
);
}
if
(code==
null
){
//throw new RuntimeException("Exception in getCrack()");
code = defaultvalue;
}
return
code;
}
// 获得破解开
@WebMethod
(operationName =
"crack"
, action =
"crack"
, exclude =
false
)
public
void
crack (
@WebParam
(partName =
"id"
)
String id,
@WebParam
(partName =
"crackValue"
)
String crackValue)
{
CrackControl c = CrackControl. getInstance();
c.setCrackCode(id, crackValue);
}
// 获得破解开
@WebMethod
(operationName =
"unCrack"
, action =
"unCrack"
, exclude =
false
)
public
void
unCrack (
@WebParam
(partName =
"id"
)
String id)
{
CrackControl c = CrackControl. getInstance();
c.unCrack(id);
}
@WebMethod
(operationName =
"getPackLength"
, action =
"getPackLength"
, exclude =
false
)
@WebResult
(name =
"returnWord"
)
public
int
getPackLength (
@WebParam
(partName =
"p"
)
byte
[] p)
{
int
value = 0;
if
(p.
length
>= 4)
{
byte
[] bytes =
new
byte
[4];
System. arraycopy(p, 0, bytes, 0, 4);
value = ConvertUtils. byteToInt(bytes);
}
return
value;
}
}
public
class
Server
{
public
static
void
main(String[] args)
throws
IOException {
CrackServerImp server =
new
CrackServerImp();
/*
* 发布Web Service
*/
String ip = InetAddress. getLocalHost().getHostAddress();
Endpoint clientEndpoint = Endpoint. publish(
"http://"
+ ip +
":7000/client"
,
server
);
Binding clientBinding = clientEndpoint.getBinding();
List<Handler> clientHandlerChain =
new
LinkedList<Handler>();
clientHandlerChain.add(
new
ClientTraceHandler());
clientBinding. setHandlerChain(clientHandlerChain);
}
}
Annotation 1:@WebService(name="client", targetNamespace="http://ws.ue.com", serviceName="client")
@WebService标签主要将类暴露为WebService,其中targetNamespace属性定义了自己的命名空间,serviceName则定义了< definitions >标签和<service>标签的name属性。
Annotation 2:@SOAPBinding(style=SOAPBinding.Style.RPC)
@SOAPBinding标签定义了WSDL文档中SOAP的消息协议,其中style属性对应SOAP的文档类型,可选的有RPC和DOCUMENT
Annotation 3:@WebMethod(operationName="getCrack",action="getCrack",exclude=false)
@WebMethod定义Web Service运作的方法,
属性action 对应操作的活动 ,如<soap:operation soapAction="getCrack" />
属性operationName匹配的wsdl:operation 的名称,如<operation name="getCrack" parameterOrder="userName">
属性exclude 用于阻止将某一继承方法公开为web服务,默认为false
Annotation 4:@WebResult(name="returnWord")
@ WebResult定义方法返回值得名称,如<part name="returnWord" type="xsd:string" />
在产生的WSDL文档中,message元素反映了这个变化:
<message name="getTimeAsString"/>
<message name="getTimeAsStringResponse">
<part name="time_response" type="xsd:string"/>
</message>
<message name="getTimeAsElased"/>
<message name="getTimeAsElasedResponse">
<part name="time_response" type="xsd:long"/>
</message>
没有添加@WebResult注解之前,WSDL文档中,message元素是这样的:
<message name="getTimeAsString"/>
<message name="getTimeAsStringResponse">
<part name="return" type="xsd:string"/>
</message>
<message name="getTimeAsElased"/>
<message name="getTimeAsElasedResponse">
<part name="return" type="xsd:long"/>
</message>
两个message元素对比,我们可以看出,return标签被换成了time_response,也就是@WebResult注解定义的。在从web服务返回的SOAP消息中也同样反映出了这点变化,以调用getTimeAsString服务方法为例:
<?xml version="1.0" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:getTimeAsStringResponse xmlns:ns2="http://ts.ch01/">
<time_response>Mon Mar 17 10:41:13 CST 2014</time_response>
</ns2:getTimeAsStringResponse>
</S:Body>
</S:Envelope>
没有添加@WebResult注解时,SOAP消息返回是这样的:
<?xml version="1.0" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:getTimeAsStringResponse xmlns:ns2="http://ts.ch01/">
<return>Mon Mar 17 10:46:30 CST 2014</return>
</ns2:getTimeAsStringResponse>
</S:Body>
</S:Envelope>
从SOAP消息对比中,我们也可以看到,return标签也相应地被替换成了time_response标签。假设我们只对getTimeAsString增加@WebResult注解,那么返回的SOAP消息中针对该服务操作使用time_response标签,而针对getTimeAsElapsed服务操作所返回的SOAP消息中仍然使用默认的return标签。
Annotation 5:@WebParam(partName="person", mode=Mode.IN
@WebParam定义方法的参数名称,如<part name="person" type="tns:person" />,其中mode属性表示参数的流向,可选值有IN / OUT / INOUT
我们提供一个范式的客户器端代码:
import
java.net.MalformedURLException;
import
java.net.URL;
import
javax.xml.namespace.QName;
import
javax.xml.ws.Service;
import
javax.xml.ws.WebEndpoint;
import
javax.xml.ws.WebServiceClient;
@WebServiceClient
(name =
"client"
, targetNamespace =
"http://ws.ue.com"
, wsdlLocation =
"http://10.0.108.194:7000/client?wsdl"
)
public
class
ClientService
extends
Service
{
private
final
static
URL
CLIENT_WSDL_LOCATION
;
static
{
URL url =
null
;
try
{
url =
new
URL(
"http://10.0.108.194:7000/client?wsdl"
);
}
catch
(MalformedURLException e)
{
e.printStackTrace();
}
CLIENT_WSDL_LOCATION
= url;
}
public
ClientService(URL wsdlLocation, QName serviceName)
{
super
(wsdlLocation, serviceName);
}
public
ClientService()
{
super
(
CLIENT_WSDL_LOCATION
,
new
QName(
"http://ws.ue.com"
,
"client"
));
}
/**
*
*
@return
returns
Client
*/
@WebEndpoint
(name =
"clientPort"
)
public
Client getClientPort()
{
return
(Client)
super
.getPort(
new
QName(
"http://ws.ue.com"
,
"clientPort"
), Client.
class
);
}
}
import
javax.jws.WebMethod;
import
javax.jws.WebParam;
import
javax.jws.WebResult;
import
javax.jws.WebService;
import
javax.jws.soap.SOAPBinding;
@WebService
(name =
"client"
, targetNamespace =
"http://ws.ue.com"
, serviceName =
"client"
)
@SOAPBinding
(style =
SOAPBinding
.Style.
RPC
)
public
interface
Client
{
// 获得破解开
@WebMethod
(operationName =
"getCrack"
, action =
"getCrack"
, exclude =
false
)
@WebResult
(name =
"returnWord"
)
public
byte
[] getCrack();
// 获得破解开
@WebMethod
(operationName =
"crack"
, action =
"crack"
, exclude =
false
)
public
void
crack(
@WebParam
(partName =
"id"
)
String id,
@WebParam
(partName =
"crackValue"
)
String crackValue);
@WebMethod
(operationName =
"getPackLength"
, action =
"getPackLength"
, exclude =
false
)
@WebResult
(name =
"returnWord"
)
public
int
getPackLength(
@WebParam
(partName =
"p"
)
byte
[] p);
// 不填了
@WebMethod
(operationName =
"unCrack"
, action =
"unCrack"
, exclude =
false
)
public
void
unCrack(
@WebParam
(partName =
"id"
)
String id);
}
import
com.ue.dto.CrackBean;
import
com.ue.ws.client.Client;
import
com.ue.ws.client.ClientService;
import
com.ue.ws.client.HandlerResolverImp;
public
class
TestClient
{
public
static
void
main(String[] args)
{
ClientService cs =
new
ClientService();
HandlerResolver hr =
new
HandlerResolverImp(
"ue"
,
"ue123456"
);
cs.setHandlerResolver(hr);
Client c = cs.getClientPort();
byte
[] bytes = c.getCrack();
byte
[] p =
new
byte
[4];
System. arraycopy(bytes, 0, p, 0, 4);
int
cl = c.getPackLength(p);
byte
[] typeName =
new
byte
[cl];
System. arraycopy(bytes, 4, typeName, 0, cl);
byte
[] content =
new
byte
[bytes.
length
-cl-4];
System. arraycopy(bytes, cl+4, content, 0, content.
length
);
CrackBean cb =
new
CrackBean(
new
String(typeName),content);
System.
out
.println(cb.getTypeName());
}
}
当然我们完全可以使用Xfire搭建基于B/S架构的web service,这个在之前的文章中已经有讲述。javax的好处是完全脱离servlet容器的支持,我们就可以搭建非web体系的服务,有时候我们选择这样做或许更好。