Servlet在web services中起到很大的作用,,負(fù)責(zé)解析soap文件(消息和附件封套),,使用wsdl來(lái)校驗(yàn)soap消息等。作為基本的編程是可以用servlet來(lái)解析soap消息,,尤其是對(duì)于帶附件的soap消息,,這樣做很透明,但是servlet不能作為Web服務(wù),,當(dāng)然拓展的JAXM是可以做到的?,F(xiàn)在那些支持web services的項(xiàng)目都是通過(guò)底層上實(shí)現(xiàn)了servlet的功能,來(lái)完成HTTP+SOAP的通信,就不需要程序員考慮基于HTTP的SOAP消息的通信過(guò)程(不要有這樣的誤解,,在java平臺(tái)上HTTP的通信的服務(wù)程序是servlet/jsp,而現(xiàn)在你不用servlet和jsp來(lái)通信,,豈不是矛盾?事實(shí)不矛盾,,記住是底層已經(jīng)實(shí)現(xiàn)了servlet功能,,具體通信有底曾自己管理),,直接上升到業(yè)務(wù)邏輯的服務(wù)編程。當(dāng)然有時(shí)也可以編寫解析SOAP消息的servlet,那就僅僅只是SOAP通信了(servlet無(wú)法作為服務(wù)來(lái)描述),。
下面根據(jù)配置文件,,來(lái)說(shuō)明一些底層與web services的關(guān)系:
回顧一下servlet的映射模式。我們知道,,servlet是從javax.servlet.http.HttpServlet繼承的,,在服務(wù)器端被載入JVM執(zhí)行,然后向客戶端輸出html流,。
servlet的web.xml文件(位于 webapps/foo/WEB-INF目錄):
"http://java./j2ee/dtds/web-app_2.2.dtd">
invoker
/servlet/*
invoker servlet 其實(shí)是:org.apache.catalina.servlets.InvokerServlet
按類名提供小服務(wù)程序,。例如,如果您調(diào)用 foo/servlet/HelloServlet,,
invoker servlet將裝入該HelloServlet(如果它在其類路徑中的話)并執(zhí)行,。
初看上面的web.xml,好像只給出了一個(gè)servlet映射,,而沒(méi)有定義invoker servlet,。
其實(shí),invoker servlet 是在tomcat的conf目錄中的web.xml中定義的::
invoker
org.apache.catalina.servlets.InvokerServlet
debug
0
2
所以,,如果拋開(kāi)Tomcat_HOME/conf/web.xml,,我們這樣定義一個(gè)web.xml,似乎更能清楚的說(shuō)明問(wèn)題:
"http://java./j2ee/dtds/web-app_2.2.dtd">
MyInvoker
org.apache.catalina.servlets.InvokerServlet
debug
0
2
MyInvoker
/servlet/*
即所有/servlet/* 模式的url,,都會(huì)交給org.apache.catalina.servlets.InvokerServlet來(lái)處理,。
或者說(shuō),所有/servlet/* 模式的url,,其實(shí)都是調(diào)用InvokerServlet這個(gè)類,,而InvokerServlet本身也是
一個(gè)servlet,它也是從 HttpServlet 繼承而來(lái)的,。
這樣,,我們自己的servlet就能夠通過(guò)特定的url執(zhí)行,即 /servlet/OurServlet,。
當(dāng)然,,如果你高興,可以定義任何的 url pattern,,而不一定是 /servlet/*,,這一點(diǎn),正如我們后面
看到的Axis處理Soap消息的方法,。
進(jìn)一步,,如果不想讓 InvokerServlet 在中間“搗鬼”,我們當(dāng)然可以直接定義自己的servlet:
"http://java./j2ee/dtds/web-app_2.2.dtd">
MyInvoker2
com.foo.MyServlet
MyInvoker2
/AnyName/*
JSP也是一樣的道理,有了上面的分析,,
看看Tomcat_HOME/conf/web.xml中的如下語(yǔ)句就可以JSP的處理方法了,,這里就不再?gòu)U話了:
....
jsp
org.apache.jasper.servlet.JspServlet
logVerbosityLevel
WARNING
3
jsp
*.jsp
....
進(jìn)入正題。
我們先來(lái)看部署Web Service的web.xml:
Axis
org.apache.axis.transport.http.AxisServlet
Axis
/services/*
所有 /services/* 模式的 url 都會(huì)交給org.apache.axis.transport.http.AxisServlet處理,,
AxisServlet當(dāng)然也是從HttpServlet繼承而來(lái)的。這就是為什么我們部署的Web服務(wù)在調(diào)用時(shí)都要在
服務(wù)名稱前加上 services/ 了,。
可以說(shuō),,AxisServlet是所有Web服務(wù)調(diào)用的入口。
那么AxisServlet在接手Web服務(wù)調(diào)用后都做了哪些工作呢,?
客戶端用call.invoke()調(diào)用web服務(wù)用的是POST,,所以入口是AxisServlet.doPost...
而不是AxisServlet.doGet...
先來(lái)看看AxisServlet的doPost函數(shù),這里只給出了關(guān)鍵語(yǔ)句及注釋:
/**
* Process a POST to the servlet by handing it off to the Axis Engine.
* Here is where SOAP messages are received
* @param req posted request
* @param res respose
* @throws ServletException trouble
* @throws IOException different trouble
*/
public void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
msgContext = createMessageContext(engine, req, res);//獲取客戶請(qǐng)求信息
engine.invoke(msgContext); //調(diào)用客戶端請(qǐng)求的服務(wù)
responseMsg = msgContext.getResponseMessage();//得到調(diào)用的返回結(jié)果
sendResponse(getProtocolVersion(req), contentType, res, responseMsg);//將結(jié)果送至客戶端
}
這樣一來(lái),,Web服務(wù)調(diào)用的來(lái)龍去脈就大致清楚了,。。,。
為了高清楚前面我們的三個(gè)url
http://192.168.0.1/test/services
http://192.168.0.1/test/services/sayHelloService?wsdl
http://192.168.0.1/test/services/sayHelloService?method=sayHelloTo&aname=everybody
是怎樣獲得輸出結(jié)果的,,再來(lái)看看AxisServlet的doGet函數(shù),,這里只給出了流程框架及注釋:
**
* Process GET requests. Because Axis does not support the GET-style
* pseudo execution of SOAP methods, this handler deals with queries
* of various kinds, not real SOAP actions.
*
* @todo for secure installations, dont stack trace on faults
* @param request request in
* @param response request out
* @throws ServletException
* @throws IOException
*/
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
//如果路徑為空,,比如:http://localhost/wstk/services 或 http://localhost/wstk/services/*
if((pathInfo == null || pathInfo.equals("")) && !realpath.endsWith(".jws"))
{
//從server-config.wsdd文件中讀取所有部署的服務(wù)信息,向向客戶端列出所有部署的服務(wù),,
//包括每個(gè)服務(wù)可調(diào)用的方法,。
}else
//如果路徑不為空,比如:http://localhost/wstk/services/sayHelloService
if(realpath != null)
{
//如果請(qǐng)求wsdl,,比如:http://localhost/wstk/services/sayHelloService?wsdl
if(wsdlRequested)
{
//創(chuàng)建sayHelloService的WSDL文件并傳送至客戶端
} else
//這里是利用url調(diào)用Web服務(wù)的入口,,比如http://192.168.0.1/test/services/sayHelloService?method=sayHelloTo&aname=everybody
if(req.getParameterNames().hasMoreElements())
{
//如果客戶端調(diào)用的方法正確,則Axis會(huì)調(diào)用相應(yīng)的JavaBean,,并把JavaBean的返回結(jié)果
//封裝為Soap消息流返回給客戶端,。
}
}
}
而Axis怎樣找到我們所請(qǐng)求的JavaBean呢?答案是server-config.wsdd文件,。
server-config.wsdd
WSDD是web service deployment descriptor的縮寫,。
最外面的元素指示這是WSDD,并定義了java的名字空間,。
接著的 元素定義了service,。一個(gè)service是一個(gè)目標(biāo)鏈,包括請(qǐng)求request,、內(nèi)容提供者provider,、響應(yīng)response。
在這個(gè)例子中,,我們指出service名字是sayHelloService ,,provider是"java:RPC",,它是axis 的標(biāo)記,指示這是一個(gè)java的RPC service,,
而處理它的真正的class是org.apache.axis.providers.java.RPCProvider,。
接著我們要在中告訴RPCProvider,它如何實(shí)例化并調(diào)用正確的class(如:com.foo.MyService),。
元素的className指示class名,,allowedMethods告訴引擎那些共用的方法要通過(guò)soap來(lái)調(diào)用。
"*"表示所有的公共方法,,我們也列出方法名字列表,,可以空格或逗號(hào)分割它們。