此文章來源:https://www.cnblogs.com/lpc-xx/p/8556827.html
一、Spring HTTP Invoker簡介
Spring HTTP invoker 是 spring 架構中的一個遠端調用模型,執行基于 HTTP 的遠端調用(意味着可以通過防火牆),并使用 java 的序列化機制在網絡間傳遞對象。這需要在遠端和本地都使用Spring才行。用戶端可以很輕松的像調用本地對象一樣調用遠端伺服器上的對象,這有點類似于 webservice ,但又不同于 webservice ,差別如下:
WebService | Http Invoker |
跨平台,跨語言 | 隻支援 java 語言 |
支援 SOAP ,提供 wsdl | 不支援 |
結構龐大,依賴特定的 webservice 實作,如 xfire等 | 結構簡單,隻依賴于 spring 架構本身 |
說明:
1. 伺服器端:通過 HTTP invoker 服務将服務接口的某個實作類提供為遠端服務
2. 用戶端:通過 HTTP invoker 代理向伺服器端發送請求,遠端調用服務接口的方法
3. 伺服器端與用戶端通信的資料均需要序列化
二、配置伺服器端和用戶端的步驟
配置伺服器端
1. 添加 springJAR 檔案
2. 建立相應的DTO(如果需要用到的話)
3. 建立服務接口
4. 建立服務接口的具體實作類
5. 公開服務
配置用戶端
1. 添加 springJAR 檔案
2. 建立相應的DTO(如果需要用到的話)
3. 建立服務接口
4. 通路服務
三、執行個體講解
配置伺服器端
先來個項目結構圖:
1). 添加 springJAR 檔案,這就不用說了,直接照着圖檔添加相應的類庫。
2). 建立服務接口和相應的DTO(Data Transmission Object)
這裡我們需要調用遠端的服務來查詢一個User對象,是以需要DTO啦。下面這個User類就是用于在網絡中傳輸的POJO類,也就是DTO啦,是以需要實作Serializable接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | |
3). UserService是一個接口,裡面定義了服務的方法,這裡面的方法将會被用戶端調用:
1 2 3 4 5 6 7 | |
4). 建立服務接口的具體實作類。這裡的UserServiceImpl是實作了UserService方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
這裡面我沒有寫DAO等層面的東西,因為那些不是這篇文章要講述的内容,因而我隻是簡單的将傳給服務端的參數封裝到對象裡的一個字段就傳回了。
5). 公開服務
下面是web.xml檔案的内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | |
這裡我們使用/service作為service的字首,那麼用戶端請求調用時需要加上這個字首,比如:
http://{host}:{port}/InvokeServer/service/{serviceName}
裡面用到的service-servlet檔案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | |
注意:
- <prop key="/userService">userServiceInvoker</prop>中的/userService是請求的服務的URL中的一部分,就是說這樣的URL會被userServiceInvoker處理
- 這裡将com.abc.invoke.server.service.UserService映射給了com.abc.invoke.server.service.impl.UserServiceImpl類了。
到此為止,伺服器算是配置好了,接下來開始配置用戶端。
配置用戶端
先來看看項目結構圖:
1). 添加 springJAR 檔案,這也不用說了,直接照着圖檔添加相應的類庫。
2). 建立服務接口和相應的DTO。
特别注意:這個類和Server端聲明的DTO要一樣,包名和字段名都要一樣才行。因為用戶端發起請求查詢User,服務端處理後先将User序列化後在傳回給用戶端,而用戶端拿到這個User後需要将其反序列化。如果包名或者字段名不同,則會被認為是不同的對象,會反序列化失敗,調用也就出錯了。我之前就是将User類的包名寫得不一樣(User類的包名在服務端為com.abc.invoke.server.bean,而在用戶端則為com.abc.invoke.client.bean),報了以下錯誤:
1 2 3 4 5 6 7 8 9 | |
很明顯可以看出,Could not deserialize result from HTTP invoker remote service......,就是因為Server端與Client端的DTO的包名不同導緻反序列化失敗。
3). 建立服務接口
這也沒啥好說的,接口和Server端定義的一樣就行,不一樣肯定報錯。可以直接将DTO和接口定義的類拷貝到用戶端即可。這個接口将會被看做是用戶端和服務端通信的“契約”。
4). 通路服務
來看看application-context.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
這裡使用了org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean這個類來作為一個service的代理類。注意到serviceUrl屬性為http://localhost:8080/InvokeServer/service/userService (當然,我在本機啟動的服務端并在本機通過main函數調用service,我在另一台機器上運作Test類的main函數,調用結果正确)。這個localhost:8080應改為實際的IP位址和端口。),這個URL的位址以/service開始,是以會被Server端攔截下來,而URL中的 /userService則為service路徑,該路徑與在Server端中service-servlet.xml中聲明的
1 | |
路徑一緻,是以這個調用會被userServiceInvoker處理。
最後再寫一個簡單的測試類Test.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
這個類也很簡單,就是從Spring的Context中取出了定義的userService這個Bean(這其實就是服務端service的一個代理類),然後直接調用該service的方法獲得結果并列印。
到此為止,用戶端配置完成。
四、啟動服務并測試
直接在項目InvokeServer上啟動Tomcat,可以看到路徑/userService的處理者是userServiceInvoker:
下面是遠端調用的執行結果:
從結果中可以看到,我代碼裡寫的名字叫Alvis,用用戶端調用服務端的service後,傳回的對象中名字是用戶端設定的名字,測試成功。
這裡是項目源代碼,供需要的朋友參考。
參考頁面:http://hanqunfeng.iteye.com/blog/868210