當大型需求被數個公司分割開來,各公司系統相互交換數據的問題就會接踵而來。
畢竟是多家不同的公司的產品,研發開發語言、采用技術框架基本上是百花齊放。
怎樣讓自家系統提供的服務具有跨平台、跨語言、跨各種防火牆,讓其他公司的研發使用起來沒障礙並且爽呢?
進過前期的技術調研和實踐,最終將目光放在了輕量級 Web Services 服務上面。

1. Web Services 服務技術的關鍵點
- XML: 做為 Web Services 的基本數據表示,特點即容易組織和分析,而且又與開發平台和語言無關。
- SOAP: 做為 Web Services 的通訊協議,基於 XML能運行在任何傳輸協議 HTTP/TCP/UDP/ 上面,將程序中的對象編碼為 XML 的規則,執行遠程調用。
TCP/HTTP/UDP 傳輸協議可以理解為寄信時信封上的格式怎么寫,貼什么郵票,SOAP 通訊協議使用 HTTP 來發送XML 格式的信息。
- WSDL: 做為描述 Web Services 的語言,你能很清晰的知道提供的服務是什么、入參、返回的數據,機器和人都很容器進行閱讀。
公司自己實現 Web Services 服務框架肯定是不現實的,如果你公司有一群熱愛技術的大牛、有一顆想造輪子的匠心,有老總的支持,還是可以放手一戰的。
目前已有的 Web Services 開發框架有: Axis、Axis2、Xfire、CXF、JAX-WS。
除了 JAX-WS 為 JDK 1.6 發布的的 Web Services 開發框架以外,其余的都是第三方開源框架。
其中 Axis 和 XFire 隨着技術更新和發展已經慢慢的談出了人們的視線,並且官方已經有很長時間沒有更新,這里暫且不表。
CXF是Apache旗下一個重磅的SOA簡易框架,它實現了ESB(企業服務總線)。
CXF 原型來源與 XFire + Client, 就像 Struts2 來自 WebWork 一樣,當然你也能預言到 XFire 的命運會和 WebWork 一樣,會淡出開發者的視線。
Axis2 是 Apache 下的一個重量級 Web Services框架,准確說它是一個Web Services / SOAP / WSDL 的引擎,是 Web Services 框架的集大成者。
Axis2 不但能制作和發布WebService,而且可以生成Java和其他語言版WebService客戶端和服務端代碼。這是它的優勢所在。
豐滿意味着臃腫和龐大,這樣的優勢也不可避免的導致了Axis2的復雜性,聽使用過她的人,它所依賴的包數量和大小都是很驚人的,打包部署發布很麻煩。
源於對第三方開源框架的恐懼和顫抖,結合自身公司的業務復雜度,技術團隊一致選定 JDK JAX-WS 作為公司的 Web Services 框架。
上述參考出處:
http://zhidao.baidu.com/link?url=H_CijOzBSzpLnZ
http://baike.baidu.com/link?url=OHYozkeWQkpZDmnKeDk9Ja
2. JAX-WS 服務端無 MVC 框架開發實踐
我這里的說的無 MVC 框架指的是沒有使用任何像 Struts/Spring MVC/WebWork....這些東西,Java EE 項目最純真的 Servlet 時代。
在 main 函數中靜態方式發布這里就不提了,確實有點 low,咱需要 Web Services 服務和 web 項目一起啟動起來,同呼吸共命運。
main 靜態發布方式參考:http://www.xuebuyuan.com/1955910.html。
-
a.maven 依賴
<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-rt</artifactId>
<version>2.2.10</version>
</dependency>
如果是非 mavn的傳統項目, 需要的 jar (大概 5M)

-
b.服務類和實現類
項目包結構

@WebService @SOAPBinding(style = SOAPBinding.Style.RPC) public interface MyFirstWebService { /** * 執行測試的WebService方法(有參) */ @WebMethod String sayHi(@WebParam(name = "name") String name); }
@WebService(endpointInterface = "com.rambo.cwe.jws.inter.MyFirstWebService") @SOAPBinding(style = SOAPBinding.Style.RPC) public class MyFirstWebServiceImpl implements MyFirstWebService { public String sayHi(String name) { return "Hi, " + name + "! "; } }
-
c.web 項目的起點,web.xml 的配置
<listener>
<listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class>
</listener>
<servlet>
<servlet-name>MyFirstWebService</servlet-name>
<servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>MyFirstWebService</servlet-name>
<url-pattern>/services/sayHi</url-pattern>
</servlet-mapping>
-
d.服務配置文件 sun-jaxws.xml,放置到 WEB-INF
<?xml version="1.0" encoding="UTF-8"?>
<endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime" version="2.0">
<endpoint name="myFirstWebService" implementation="com.rambo.cwe.jws.impl.MyFirstWebServiceImpl" url-pattern="/services/sayHi"/>
</endpoints>
注意服務配置文件中的 url-pattern 和 servlet 中的 url-pattern 是一致的。
通過上面的4步的操作,啟動 web 項目吧,不出什么意外的話,啟動 web 容器打印日志是這樣。

瀏覽器訪問 http://localhost:5050/cwe/services/sayHi
示例項目: https://github.com/OrsonEx/cwe

3. JAX-WS 服務端與 Spring 集成開發實踐
在 Spring 中集成相當簡單,分分鍾配置集成。
-
a.mavn 依賴需要添加的支持 jar(500K)
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-core</artifactId>
<version>2.3.0.RELEASE</version>
</dependency>
-
b.Spring Bean 添加 Web Services 服務支持
<bean class="org.springframework.remoting.jaxws.SimpleJaxWsServiceExporter">
<property name="baseAddress" value="http://localhost:8089/services/"/>
</bean>
地址你隨意,配置為和 web 項目一致也沒問題。
-
c.服務實現類 Bean 配置
大體的意思是你得讓 Spring 知道有這樣一個服務實現類。
如果你比較傳統,可以配置在 Spring-***.xml 中,像這樣
<bean id="myFirstWebServiceImpl" class="com.rambo.cwe.jws.impl.MyFirstWebServiceImpl"> ... </bean>
當然你也可以,使用注解,像這樣

簡單的三步之后,把你的 web 容器啟動起來吧,Web Services 服務就發布成功了。
實例工程:https://github.com/OrsonEx/sme-jws
4. 客戶端 JAX-WS 服務的調用
服務端編寫好之后,使用 JDk 自帶的 wsimport 工具生成客戶端代理類。
wsimport 只需要指定 url 就能生成 java 調用的客戶端,你完全不用去了解這個 WebServer 服務是用什么語言編寫的。
wsimport -s f: http://localhost:5050/cwe/services/sayHi?wsdl
wsimport 常用參數參考:
| 參數 | 說明 |
| -p | 定義客戶端生成類的包名稱 |
| -s | 指定客戶端執行類的源文件存放目錄 |
| -d | 指定客戶端執行類的class文件的存放目錄 |
| -keep | 表示生成客戶端執行類的源代碼 |
| -b | 指定jaxws/jaxb綁定文件或額外的schemas |
| -extension | 使用擴展來支持SOAP1.2 |
補充: 關於 wsimport 生成代理類:http://blog.csdn.net/aqsunkai/article/details/51711087
生成的代理類(生成姿勢多種多樣,自行參考上面常用參數,沒有標准只有最適合):

將代理類放入客戶端項目,只需要兩行代碼就能實現 Web Services 服務的調用,如下
MyFirstWebServiceImplService service = MyFirstWebServiceImplService(); MyFirstWebService implPort = service.getMyFirstWebServiceImplPort(); System.out.println(implPort.sayHi("Orson"));
其實我一直想嘗試,不往客戶端添加任何代碼,調用 Web Services 服務,很遺憾沒能成功。
最接近的的是,在客戶端放入服務端的接口類,代碼可以這樣寫。
URL url = new URL("http://localhost:5050/cwe/services/sayHi?wsdl"); QName serviceName = new QName("http://impl.jws.cwe.rambo.com/", "MyFirstWebServiceImplService"); QName portName = new QName("http://impl.jws.cwe.rambo.com/", "MyFirstWebServiceImplPort"); Service service = Service.create(url,serviceName); MyFirstWebService servicePort = service.getPort(portName,MyFirstWebService.class); System.out.println(servicePort.sayHi("Orson"));
自行定義連接地址、服務名稱、、端口名稱,生成服務實例進行訪問,代碼完全沒有使用代理類那樣清晰。
等下次,你們公司需要給其他公司提供服務時,只需要編寫好 Web Services,抽象好接口,擼好實現層。
直接甩一個地址給其他公司,順便可以把我這篇博客地址發過去,然后整個世界就安靜了。
