本文不對jax-ws使用的細節進行講解(隻撈幹貨),請諒解。
采用的開發架構 : jax-ws
特點:服務端與用戶端都是直接操作 SOAP 的全部消息内容。免去不必要的網絡傳輸和xml的串行/解串行
約定的URI:http://[host]:[port]/[web-app-name]/service/[business-name]/[select|delete|update|insert]?[query-strings]
業務名稱(business-name)在URI上展現(借用不合适的詞彙--資源)
業務操作隻規定四種: select(查詢),delete(删除),update(更改),insert(新增) 。業務操作在URI展現出來(RPC形式)
實作的原理:根據約定的URI反射出業務的實作類(根據URI上的business-name) ;根據URI上的操作([select|delete|update|insert])定位到具體的業務實作類上的方法并執行
先來看服務的入口
@ServiceMode(value = Service.Mode.MESSAGE)
@WebServiceProvider(serviceName = "TimingSOAPWS", portName = "TimingSOAPWSPort", targetNamespace = "http://www.timing.com")
@BindingType(value = SOAPBinding.SOAP12HTTP_MTOM_BINDING)
@MTOM(enabled = true, threshold = 10 * 1024 * 1024)//10M的檔案才啟用MTOM
public final class TimingSOAPWS implements AsyncProvider<SOAPMessage> {
public void invoke(SOAPMessage request, AsyncProviderCallback<SOAPMessage> callback, WebServiceContext context) {
javax.servlet.ServletContext servletContext = (javax.servlet.ServletContext)context.getMessageContext().get(MessageContext.SERVLET_CONTEXT);
final Runnable processRunnable = new ProcessRunnable(request, callback, context);
//如果 servlet 環境裡有線程池,則使用線程池
if (servletContext != null) {
Object o = servletContext.getAttribute(WSProviderThreadPoolListener.THREAD_POOL_ATTR);
if (o instanceof ExecutorService)
((ExecutorService)o).execute(processRunnable);
}
else//沒有線程池,則啟動新線程
new Thread(processRunnable).start();
}
}
接下來,抽象出業務的操作
import javax.xml.ws.*;
/**
* 抽象業務處理器。定義了 插入,更新,删除,查詢
* @author Hardneedl
*/
public interface WSOperator <S, R> {
/**
* 插入資料的操作
* @param request 請求的封包
* @param context web 上下文環境 @return 将要向用戶端打回的内容
*/
R insert(S request, WebServiceContext context) throws Exception;
/**
* 更新資料的操作
* @param request 請求的封包
* @param context web 上下文環境
* @return 将要向用戶端打回的内容
*/
R update(S request, WebServiceContext context) throws Exception;
/**
* 删資料的操作
* @param request 請求的封包
* @param context web 上下文環境
* @return 将要向用戶端打回的内容
*/
R delete(S request, WebServiceContext context) throws Exception;
/**
* 查資料的操作
* @param request 請求的封包
* @param context web 上下文環境
* @return 将要向用戶端打回的内容
*/
R select(S request, WebServiceContext context) throws Exception;
}
最後來看關鍵的實作代碼
final class ProcessRunnable implements Runnable {
private SOAPMessage request;
private AsyncProviderCallback<SOAPMessage> callback;
private WebServiceContext context;
ProcessRunnable(SOAPMessage request, AsyncProviderCallback<SOAPMessage> callback, WebServiceContext context) {
this.request=request;
this.callback=callback;
this.context=context;
}
public void run() {
MessageContext msgContext = context.getMessageContext();
String pathString=(String)msgContext.get(MessageContext.PATH_INFO);
if (pathString==null||pathString.isEmpty())return;
String pathInfos[] = pathString.split("/");
try {
WSOperator opt = ClzManagerFactory.getClzManager().getOperatorSingleInstance("com.timing.business."+pathInfos[1] + "Operator");
Method method = opt.getClass().getDeclaredMethod(pathInfos[2], SOAPMessage.class, WebServiceContext.class);
SOAPMessage soapMessage = (SOAPMessage)method.invoke(opt,request,context);
callback.send(soapMessage);
}catch (Exception e) {
callback.sendError(e);
}
}
}
相比 WSDL->java的方式有何優勢?
1) 就四種操作方法,服務的操作簡單清晰
2) 複雜業務情形下,用戶端post大型内容到服務端的時候,直接操作xml,免去jax-ws runtime 對于 xml-> java 類型的解串行操作。解串行操作消耗了更多的cpu和記憶體
3) 簡單業務需求的時候,直接在 URI 上用查詢字元串提供參數,這也是免去jax-ws runtime 對于 xml-> java 類型的解串行操作
wsdl->java 的開發方式中,服務端即使傳回一個Integer類型,都需要 java->xml,然後用戶端 xml->java
WSOperator的一個示範實作
/*這是一個擴充卡,具體業務實作者繼承自這個類*/
public class WSOperatorAdapter implements WSOperator<SOAPMessage, SOAPMessage> {
public SOAPMessage insert(SOAPMessage request, WebServiceContext context) throws Exception {return request;}
public SOAPMessage update(SOAPMessage request, WebServiceContext context) throws Exception {return request;}
public SOAPMessage delete(SOAPMessage request, WebServiceContext context) throws Exception {return request;}
public SOAPMessage select(SOAPMessage request, WebServiceContext context) throws Exception {return request;}
}
final public class SiteInfoOperator extends WSOperatorAdapter{
static private final Config CONFIG = ConfigFactory.getTextConfig();
/**
* 輸出的 xml 結構
* <SiteInfo pollutantSourceCode=""
* drainOutletCode=""
* drainOutletName=""
* equipmentName=""
* deviceName=""
* scales=""
* groupType=""
* isNormal=""
* remarks=""
* userId=""
* itemClass=""/>
*
*
* @param request
* @param context
* @return
* @throws Exception
*/
public SOAPMessage insert(SOAPMessage request, WebServiceContext context) throws Exception {
String xmlString = ConvertorFactory.createSOAPBodyStringConvertor("SiteInfo").convert(request);
Map<String,String> param = new HashMap<>(1);
param.put("xml", xmlString);
DaoFactory.createInsertDao("siteinfo.insert").perform(param);
return WSMessageTool.createSimpleTextMessage(
ConfigFactory.getTextConfig().getString("db.insert.ok")
);
}
}