前言
在 Internet 網絡覆蓋全球的今天
網絡通信已經是當今軟件開發過程中離不開的話題
在常用的Windows
Liunx
Unix 系統當中
大部分的網絡數據傳輸都是使用 TCP/IP
UDP/IP 作為底層傳輸協議的
而 HTTP 協議就是基於 TCP/IP 協議而運行的超文本傳送協議
在 JAVA 高級開發語言中
陸續出現 RMI
CORBA
JAX
RPC
JAX
WS
Axis
XFire
HTTP Invoker
Hessian
Burlap
JMX 等遠程通信架構去實現系統之間數據傳送
在
遠程通信技術
的一系列文章中
本人將對上述復雜的 JAVA 遠程通信技術作出歸納
首先
在本篇文章中先對有著
多年歷史的 Axis 進行介紹
一Axis 簡介
Web 服務的起源
Web 服務是現今實現網絡服務概念的趨勢
它把基礎架構建立於標准化的XML語言之上
能夠使用一種與平台無關的方式對數據進行編碼
其中 SOAP 與 WSDL 都遵從此標准化的 XML 編碼規則
SOAP (Simple Object Access Protocol
簡單對象訪問協議)是一種輕量的
簡單的
基於 XML 的協議
用於描述在服務過程中服務器端與客戶端之間所交換的消息
SOAP 可以和現存的許多因特網協議和格式結合使用
包括超文本傳輸協議( HTTP)
簡單郵件傳輸協議(SMTP)
多用途網際郵件擴充協議(MIME)
WSDL (Web Service Definition Language
Web服務描述語言)是一種基於 XML的協議
用於定義服務端與客戶端之間的契約
描述Web服務的公共接口
列出 Web服務進行交互時需要綁定的協議和信息格式
Web 服務采用 WSDL 語言描述該服務支持的操作和信息
運行時再將實際的數據以 SOAP 方式在服務端與客戶端進行信息傳遞
由於軟件開發平台眾多
當中存在不同的開發風格
當服務器端與客戶端使用不同的開發工具時
數據轉換成為復雜且關鍵的問題
而 SOAP 與 WSDL 的主要特性之一在於它們都是可擴展的
且與開發平台無關
為了建立統一的 XML 協議
微軟
IBM
Sun
Oracle
BEA 等多家軟件開發商聯合起來
組成了一個名為WS
I(Web Service Interoperability)組織
由該組織制定 WS
ReliableMessage
WS
Discovery
WS
Federation
WS
Coordination
WS
AtomicTransaction
WS
BusinessActivity
WS
Enumeration
WS
Eventing
WS
Management 等一系列用於數據交換的規范
JAX
RPC
JAX
WS 簡介
JAX
RPC ( Java API for XML
based RPC ) 是 Java 庫中基於 XML 遠程服務的一組標准 API
它通過 WSDL 方式對所提供的服務進行描述
並以 RPC 的風格把 SOAP 信息進行公開
是 Java 庫中最早對 Web 服務提供支持的一組API
JAX
RPC
從其名稱可以看出
最初的目的只是為了支持使用(Remote Procedure Call
RPC) 的 XML 遠程過程調用操作
它以 BP
(WS
I
s Basic Profile
)為基礎
依賴於SAAJ
(SOAP with Attachments API for Java)為規范
雖然支持 SOAP 協議
但對 Web 服務功能有一定的局限性
於是在
年底
開發團隊對 JAX
RPC
進行大幅修訂
由 Sun 公司組織了一個專家組開始進行 JAX
RPC
規范的開發
JAX
RPC
是基於 JAVA
而開發的
它依賴於 Annotation 等新特性
在JAX
RPC 的基礎上提供還增加了如異步回調
面向消息等新增技術
JAX
RPC
以 BP
(WS
I
s Basic Profile
) 為基礎
依賴於SAAJ
(SOAP with Attachments API for Java)為規范
能使用 SOAP
SOAP
進行信息公開
它是 JAX
RPC
架構發展的成果
在開發完成後
JAX
RPC
被正式改名成為 JAX
WS ( Java API for XML
Web Services )
Axis 概述
Axis 全稱 Apache EXtensible Interaction System ( 阿帕奇可擴展交互系統 )
它是一個 SOAP 引擎
提供創建 Web 服務的基本框架
Axis
x 是基於 JAX
RPC 而實現一個工具包
它可以使用 HTTP
JMS
SMTP 等多種傳輸方式支持 SOAP
Axis
x 是新一代的 Axis 引擎
它支持 JAX
WS
JAX
PRC 等 API
並且在Axis
x 的基礎上增加了靈活數據綁定
異步調用等新增功能
可使用 SOAP
SOAP
協議
在服務請求上
Axis
x 支持三種請求
響應模式
In
Only
Robust
In和In
Out
也可支持使用 REST 風格的開發方式
基本的 Axis Web 服務由四部分組成
Axis Servlet
Axis 部署描述
遠程服務接口
服務實現類
Axis Servlet 是 Axis 的核心
它負責 WSDL 基礎服務信息的公開
並把 SOAP 請求轉化為 Java 方法的調用
最後把返回值轉化為 SOAP
Axis Servlet 隱藏了構建 Web 服務的大量代碼
使用開發人員不用直接與 SOAP 打交道便可輕松完成 Web 服務的開發
Axis 部署描述是一個XML文檔
它用於管理 Web 服務的發布
決定哪些服務類需要通過 SOAP 對外公開
遠程服務接口並非必要的
但在很多的 Web 服務開發過程中都會使用遠程服務接口用於對外暴露服務類的方法
在服務器端通過服務實現類去繼承實現服務接口
由於 Axis
x 與 Axis
x 有各自的特色
下面將分開來介紹
回到目錄
二Axis x 實例
Axis
x 的下載與安裝
Axis
x 可於官網 下載
完成下載後建立 Web Project 作為測試項目
把 lib 文件夾下的 jar 文件拷貝
引入到測試項目當中
在 web
xml 文件下加入 AxisServlet 配置後
系統就會對以後綴為 *
jws 及路徑為 /services/* 的請求進行監聽
遇到此類請求時將把信息交由 org
apache
axis
transport
http
AxisServlet 進行處理
<web
app>
<display
name>Apache
Axisdisplay
name>
<listener>
<listener
class>org
apache
axis
transport
http
AxisHTTPSessionListenerlistener
class>
listener>
<servlet>
<display
name>Apache
Axis Servletdisplay
name>
<servlet
name>AxisServletservlet
name>
<servlet
class>
org
apache
axis
transport
http
AxisServlet
servlet
class>
servlet>
<servlet>
<display
name>Axis Admin Servletdisplay
name>
<servlet
name>AdminServletservlet
name>
<servlet
class>
org
apache
axis
transport
http
AdminServlet
servlet
class>
<load
on
startup>
load
on
startup>
servlet>
<servlet>
<display
name>SOAPMonitorServicedisplay
name>
<servlet
name>SOAPMonitorServiceservlet
name>
<servlet
class>
org
apache
axis
monitor
SOAPMonitorService
servlet
class>
<init
param>
<param
name>SOAPMonitorPortparam
name>
<param
value>
param
value>
init
param>
<load
on
startup>
load
on
startup>
servlet>
<servlet
mapping>
<servlet
name>AxisServletservlet
name>
<url
pattern>/servlet/AxisServleturl
pattern>
servlet
mapping>
<servlet
mapping>
<servlet
name>AxisServletservlet
name>
<url
pattern>*
jwsurl
pattern>
servlet
mapping>
<servlet
mapping>
<servlet
name>AxisServletservlet
name>
<url
pattern>/services/*url
pattern>
servlet
mapping>
<servlet
mapping>
<servlet
name>SOAPMonitorServiceservlet
name>
<url
pattern>/SOAPMonitorurl
pattern>
servlet
mapping>
web
app>
而 SOAPMonitorService 並非必要配置
但加入SOAPMonitorService 配置
可以便於對服務運行時所傳輸的SOAP信息進行監聽
在服務的requestFlow或responseFlow 中加入 SOAPMonitorHandler
系統就可顯示服務請求和回發時的 SOAP 信息
<deployment xmlns=
xmlns:java=
>
<handler name=
soapmonitor
type=
java:org
apache
axis
handlers
SOAPMonitorHandler
>
<parameter name=
wsdlURL
value=
/axis/SOAPMonitorService
impl
wsdl
/>
<parameter name=
namespace
value=
impl
wsdl
/>
<parameter name=
serviceName
value=
SOAPMonitorService
/>
<parameter name=
portName
value=
Demo
/>
handler>
<service name=
SOAPMonitorService
provider=
java:RPC
>
<parameter name=
allowedMethods
value=
publishMessage
/>
<parameter name=
className
value=
org
apache
axis
monitor
SOAPMonitorService
/>
<parameter name=
scope
value=
Application
/>
service>
<service name=
myService
provider=
java:RPC
>
<requestFlow>
<handler type=
soapmonitor
/>
requestFlow>
<responseFlow>
<handler type=
soapmonitor
/>
responseFlow>
<parameter name=
allowedMethods
value=
*
/>
<parameter name=
className
value=
axis
server
myService
/>
service>
deployment>
調用服務的三種方式
下面從最簡單的 HelloWorld 開始
介紹 Axis 的使用方法
首先在 WEB
INF 文件夾下建立 server
config
wsdd 文件
在 Axis
x 當中
此文件正是用於管理服務發布的默認配置文件
首先 service 用於定義對外暴露的服務
其中 name 屬性用於定義服務的名稱
像下面例子
當 name 為 PersonService 時
對外暴露的服務路徑則對應為
而 parameter 用於定義服務的相關屬性
className 表示此服務的實現類
而 allowedMethods 表示所公開的服務方法
*
則默認為公開此類中的所有 public 公共方法
而 scope 則是用於定義服務對象生成的方式
它包括三個選項
request
session
application
request 是默認選擇
表示為每個請求生成一個服務對象
session 表示對同一個客戶代理對象所發送的請求使用同一個服務對象
並把服務信息放在同一個上下文當中
當使用有狀態服務時
使用此 session 更為合適
在下節將再作進一步介紹
application 類似於使用單體模式
表示所示的請求均使用同一個服務對象
當使用無狀態服務時使用 application 能有效提高運行效率
最後在 transport 中定義一個 requestFlow 處理類 org
apache
axis
handlers
http
URLMapper
表示在系統接受到 http 請求時
將會調用 URLMapper 類來處理路徑映射等問題
<deployment xmlns=
xmlns:java=
>
<service name=
PersonService
provider=
java:RPC
>
<parameter name=
className
value=
axis
serviceImpls
PersonServiceImpl
/>
<parameter name=
allowedMethods
value=
*
/>
<parameter name=
scope
value=
application
/>
service>
<transport name=
http
>
<requestFlow>
<handler type=
java:org
apache
axis
handlers
http
URLMapper
/>
requestFlow>
transport>
deployment>
PersonService 服務代碼如下
此時運行服務
輸入路徑
浏覽器上將顯示此服務的 wsdl 信息
public interface IPersonService {
String HelloWorld(String name)
}
public class PersonServiceImpl implements IPersonService {
@Override
public String HelloWorld(string name){
return
Hello
+name;
}
}
客戶端的生成工具有多種
其中一種是使用 Axis
x 中的自帶生成器 WSDL
Java
此生成器可以根據 wsdl 文件生成客戶端
首先在環境變量中把 Axis_Home 綁定到 Axis
x 的根目錄
在 path 加入設置
;%Axis_Home%\lib
然後輸入
Java org
apache
axis
wsdl
WSDL
Java
p axis
client
person
此時系統將在 axis
client
person 包內生成客戶端代碼類 PersonServiceImpl
PersonServiceImplService
PersonServiceImplServiceLocator
PersonServiceSoapBindingStub
PersonServiceImplServiceLocator 用於實現 PersonServiceImplService 接口
它綁定了服務的名稱
地址
端口等詳細資料
PersonServiceImpl 用於定義服務的接口
而 PersonServiceSoapBindingStub 則是此服務代理
它通過 SOAP 協議把操作請求發送到服務器
並把返回信息轉化為 Java 對象
public static void main(String[] args) throws
RemoteException
MalformedURLException
ServiceException {
// TODO Auto
generated method stub
HelloWorld()
}
private static void HelloWorld() throws
RemoteException
MalformedURLException{
PersonServiceImpl personService=new PersonServiceSoapBindingStub(
new URL(
)
new PersonServiceImplServiceLocator())
System
out
println(personService
helloWorld(
Leslie
))
}
通過系統自動生成的代理類就能簡單地調用遠程服務
這是因為在代理類中已經完成了大量關於PersonService 服務的配置
但本人覺得想要深入地了解 Axis 的開發
就應該了解其內部的結構
所以在下面例子當中將介紹如何使用Axis的內部機制直接調用Web服務
在 org
apache
axis
client 中存在著服務的基礎類 Service
通過 Service 可用於管理服務的綁定地址
端點
獲取服務的 WSDL 等詳細信息
另外 Call 類用於管理每個服務請求動作
它可以設置每個請求的方法
最後通過call
invoke(Object[])調用服務
並獲取完成後的返回值
public static void main(String[] args) throws
RemoteException
MalformedURLException
ServiceException {
HelloWorld()
}
private static void HelloWorld() throws
ServiceException
RemoteException{
//生成服務對象Service
Service service=new Service()
Call call=(Call) service
createCall()
//設置Endpoint地址
call
setTargetEndpointAddress(
)
//綁定請求方法名稱
call
setOperationName(
HelloWorld
)
//通過call
invoke調用服務
獲取返回值
String data=(String)call
invoke(new Object[]{
Leslie
})
System
out
println(data)
}
如果覺得使用 Call 實現請求較為麻煩
Service 中還提供一個 getPort 方法
通過此方法還可直接實現服務接口 PersonServiceImpl
另外
Axis 還為准備了一個 ServiceFactory 工廠
通過 ServiceFactory 可以直接獲取 Service 對象
public static void main(String[] args) throws
RemoteException
MalformedURLException
ServiceException {
// TODO Auto
generated method stub
HelloWorld()
}
private static void HelloWorld() throws
ServiceException
RemoteException
MalformedURLException {
String wsdl=
;
String uri=
;
String serviceName=
PersonServiceImplService
;
//使用serviceFacotry直接生成服務
ServiceFactory factory=ServiceFactory
newInstance()
Service service=(Service) factory
createService(
new URL(wsdl)
new QName(uri
serviceName))
//使用service
getPort方法實現服務接口
PersonServiceImpl personService=(PersonServiceImpl)service
getPort(PersonServiceImpl
class)
String data=oWorld(
Leslie
)
System
out
println(data)
}
以自定義對象傳輸數據
若需要以自定義對象作為數據傳輸的載體
則需要為自定義對象繼承 Serializable 接口
另外可以留意一下服務的 wsdl
因為 Axis 並沒有默認使用List
Map 等類型
在 List
Map 等作為參數時
wsdl 都會把返回類型設置為 ArrayOf_xsd_anyType
所以建議使用簡單數組作為返回值
public class PersonEntity implements Serializable {
private Integer id;
private String name;
private Integer age;
private String address;
public PersonEntity(Integer id
String name
Integer age
String address){
this
id=id;
this
name=name;
this
age=age;
this
address=address;
}
public Integer getId(){
return id;
}
public void setId(Integer id){
this
id=id;
}
……
}
public interface IPersonService {
PersonEntity GetPerson(int id)
PersonEntity[] GetList()
List GetList(String name)
}
public class PersonServiceImpl implements IPersonService {
@Override
public PersonEntity[] GetList(){
PersonEntity[] list=new PersonEntity[
];
PersonEntity person
=new PersonEntity(
Leslie
tianhe
)
PersonEntity person
=new PersonEntity(
Elva
henan
)
list[
]=person
;
list[
]=person
;
return list;
}
@Override
public List GetList(String name){
List list=new ArrayList()
PersonEntity person
=new PersonEntity(
name+
Lee
tianhe
)
PersonEntity person
=new PersonEntity(
name+
Chen
henan
)
list
add(person
)
list
add(person
)
return list;
}
@Override
public PersonEntity GetPerson(int id){
return new PersonEntity(id
Leslie
tianhe
)
}
}
在 server
config
wsdd 中使用 beanMapping 加入自定義對象綁定
以 languageSpecificType 綁定類名
qname 可由用戶設置
但必須與 xmln 特性相對應
……
<service name=
PersonService
provider=
java:RPC
>
<parameter name=
className
value=
axis
serviceImpls
PersonServiceImpl
/>
<parameter name=
allowedMethods
value=
*
/>
<parameter name=
scope
value=
application
/>
<beanMapping qname=
myNS:PersonEntity
xmlns:myNS=
urn:PersonEntity
languageSpecificType=
java:axis
entity
PersonEntity
/>
service>
……
以 WSDL
Java 生成客戶端代碼後
可以留意 PersonEntity 對象已經自動實現了 Serializable 接口
增加了 getDeserializer
getSerializer 等序列化與反序列化方法
此時直接使用代理類
會自動地完成對象序列化的過程
可以節省了不少時間
public static void main(String[] args) throws
RemoteException
MalformedURLException
ServiceException {
GetList()
GetPerson()
}
private static void GetPerson() throws
RemoteException
MalformedURLException{
PersonServiceImpl personService=new PersonServiceSoapBindingStub(
new URL(
)
new PersonServiceImplServiceLocator())
PersonEntity person=personService
getPerson(
)
DisplayPersonProperty(person)
}
private static void GetList() throws
ServiceException
RemoteException
MalformedURLException{
PersonServiceImpl personService=new PersonServiceSoapBindingStub(
new URL(
)
new PersonServiceImplServiceLocator())
Object[] objs=personService
getList(
Leslie
)
for(Object person:objs)
DisplayPersonProperty((PersonEntity)person)
}
//顯示對象屬性
private static void DisplayPersonProperty(PersonEntity person){
System
out
println(
Id:
+person
getId()+
Name:
+person
getName()+
Age:
+
person
getAge()+
Address:
+person
getAddress())
}
但需要注意
如果使用 Service 類去調用服務的時候
需要使用 Call
registerTypeMapping 注冊一個類型
把接收到的信息轉換為 PersonEntity 類型
在注冊類型時 namespaceURI 參數值需要與服務端 server
config
wsdd 中的值保持一致
public static void main(String[] args) throws
RemoteException
MalformedURLException
ServiceException {
// TODO Auto
generated method stub
GetArray()
GetList()
}
private static void GetArray() throws
ServiceException
RemoteException{
Service service=new Service()
Call call=(Call)service
createCall()
call
setTargetEndpointAddress(
)
//注冊返回類型
namespaceURI 必須與服務端注冊值一致
QName qName
=new QName(
urn:PersonEntity
PersonEntity
)
call
registerTypeMapping(PersonEntity
class
qName
new BeanSerializerFactory(PersonEntity
class
qName
)
new BeanDeserializerFactory(PersonEntity
class
qName
))
//綁定請求方法
call
setOperation(
GetList
)
//設置返回類型
call
setReturnClass(PersonEntity[]
class)
PersonEntity[] list=(PersonEntity[]) call
invoke(new Object[]{})
for(PersonEntity person:list)
DisplayPersonProperty(person)
}
private static void GetList() throws
ServiceException
RemoteException{
Service service=new Service()
Call call=(Call)service
createCall()
call
setTargetEndpointAddress(
)
//注冊返回類型
namespaceURI 必須與服務端注冊值一致
QName qName
=new QName(
urn:PersonEntity
PersonEntity
)
call
registerTypeMapping(PersonEntity
class
qName
new BeanSerializerFactory(PersonEntity
class
qName
)
new BeanDeserializerFactory(PersonEntity
class
qName
))
//綁定請求方法
call
setOperationName(new javax
xml
namespac
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26177.html