保持領先
VMware 提供培訓和認證,助您快速提升。
瞭解更多這是該系列中唯一一篇實際使用了 Spring Framework 的部落格。Spring 用於配置 Spring Dynamic Modules 以及釋出和消費 OSGi 服務。它還展示了一種連線 Spring 管理的 bean 與 GWT 遠端處理的方式。然而,我很清楚 Spring/GWT 整合本身就是一個重要主題,所以我在此有意只提供一個簡單的解決方案。
關於 GWT StockWatcher 示例及其我正在使用的軟體的背景資訊,請參閱第 1 部分。
另請注意,您可以跳過所有這些繁瑣的說明,直接檢視底部的下載彙總。
在第 2 部分中,我們從 WAR 檔案中移除了 GWT 依賴項,並將它們轉換為一個 OSGi 包,該包被安裝到 dm Server 的倉庫中。完成此操作後,我們就可以部署任意數量使用 GWT 遠端處理的應用,而無需在 WAR 中包含任何 GWT 依賴項。
在本最終部分中,我們將使用 Spring Dynamic Modules 進一步模組化應用。您可能會問一個問題……為什麼?這不是一個壞問題——沒有人願意為了模組化而使程式碼不必要地複雜化。在決定共享服務方法是否有幫助時,可以將其歸結為幾個簡單的問題:
- 應用的任何部分是否可能需要被其他應用使用? - 應用中的不同元件是否會以不同的速度演變? - 是否可能需要維護同一組件的多個併發版本? - 是否希望能夠在應用執行時部署元件的更改?
看看我們的 Stockwatcher 應用,它目前僅限於一個股市。我們知道世界各地有許多不同的市場,為了使其更靈活,使其能夠訪問不同的市場肯定是有意義的,所以這就是我們要做的——將我們的股市轉換為共享服務。一個包將包含一個共享 API,該 API 定義了市場可以做什麼。其他包可以是該 API 的實現——也許一個是倫敦市場,一個是紐約市場。然後我們將能夠部署我們想要的市場,啟動、停止、取消部署和替換它——所有這些都在執行時進行。
我意識到將單個 WAR 檔案轉換為共享服務的過程可能比從零開始以模組化方式設計更為常見,因此本部落格將進一步基於我們在第 2 部分中完成的工作,而不是從頭開始。您可以在此處下載第 2 部分的 Eclipse 專案,在此處下載第 3 部分所有工作完成後的專案(如果下載,需要定義 GWT_ROOT_INSTALL classpath 變數)。希望我在早期部落格中關於劃分原始碼的一些建議最終會變得有意義!
要在 Eclipse 中建立一個簡單的包專案,右鍵單擊並選擇 New->Other->SpringSource dm Server->Bundle Project。這將為您建立一個骨架MANIFEST.MF以便開始使用。我們無需將其設為 Spring Dynamic Module,因為它唯一的目的是提供 API,並且不會包含自己的任何 bean 例項,因此普通包即可。
- 將專案名稱指定為 StockWatcherServiceAPI - 如果您願意,可以透過單擊 Project Layout 中的 Configure Default... 連結來配置源目錄。為保持一致性,我將其設定為src/main/java- 將包名稱和符號名稱設定為com.google.gwt.sample.stockwatcher.client.api版本保留為1.0.0- 模組型別應為None目標執行時應為您的 dm Server 例項。如果無法選擇此選項,需要單擊新建並建立一個。 - 現在可以單擊完成,應該會看到新的包專案以及生成的MANIFEST.MF.
下一步是移動 API 程式碼到新專案中。將整個com.google.gwt.sample.stockwatcher.client.api包從 StockWatcherWar 專案拖放到新的包專案中。這是客戶端和伺服器共享的所有程式碼。它應該包含StockPriceService介面以及StockPrice和DelistedException類。如果成功,幾乎所有東西都會損壞!
接下來,考慮一下我們需要從新包中匯出什麼。在這種情況下很簡單——一個包中的 API。在MANIFEST.MF編輯器的 Runtime 選項卡中,將該包新增到 Exported Packages 列表中並儲存。
現在需要考慮 API 程式碼需要哪些依賴項,換句話說,為什麼它不再構建了。顯然,我們在第 2 部分中建立的 GWT 包是一個好的開始。在 Dependencies 選項卡中,將 com.google.gwt 包新增到 Import Bundle 列表中(如果未列出,可能不在 dm Server 倉庫中,這在第 2 部分中解釋了)並儲存。這應該會修復新專案中的所有構建問題,但其他專案應該仍然損壞。暫時忽略這一點,因為還有更多重構工作要做。
此時,StockWatcherServiceAPI 專案應該看起來像這樣
它的MANIFEST.MF應該看起來像這樣
使用步驟 1 中的說明建立一個新的包專案,命名為 "StockWatcherServiceLondon",並給它一個包名稱 com.google.gwt.sample.stockwatcher.service.london。
所以我們來考慮一下這個包需要什麼。它需要實現倫敦股市服務的程式碼,以及一種將該服務匯出到 OSGi 註冊中心作為共享服務的方式。第一部分是一個簡單的重構工作,因為我們已經有一些可以重用的服務程式碼。對於第二部分,將包設為 Spring Dynamic Module 將是最簡單的方法,因為它使得使用 Spring bean 釋出和消費 OSGi 服務非常簡單。
首先。回頭看看StockPriceServiceImpl.java中的服務實現。會看到StockPriceService程式碼與RemoteServiceServlet繫結。servlet 需要保留才能使遠端處理工作,但我們需要提取實現程式碼並用它來建立共享服務。在第 4 部分中,需要調整 servlet 來消費我們建立的服務。
所以,複製com.google.gwt.sample.stockwatcher.server包從 StockWatcherWar 專案,並貼上到 StockWatcherServiceLondon 的src/main/java中。現在應該有一個StockPriceServiceImpl.java的副本,可以進行重構。
刪除extends RemoteServiceServlet、RemoteServiceServletimport和SerialVersionUID
欄位,因為這些現在都不再需要了。儲存更改時,會看到需要匯入一些依賴項,特別是步驟 1 中建立的 API 包。MANIFEST.MF編輯com.google.gwt.sample.stockwatcher.client.api以新增 Import Package 來匯入MANIFEST.MF。儲存時,應該會看到清單檔案中出現錯誤,說無法解析該包。我們做錯了什麼?!問題在於,當 Eclipse 中的包專案之間存在匯入和匯出時,需要告訴工具允許專案共享引用。右鍵單擊 StockWatcherServiceLondon,選擇 Properties->Project References。勾選 StockWatcherServiceAPI 的複選框,然後單擊 OK。然後需要假編輯
以使更改生效。現在應該看到 StockWatcherServiceAPI 列為 Bundle Dependency。讓我澄清一點,一旦包從 Eclipse 匯出,就無需執行此額外步驟——僅當需要在它們只是專案時建立包之間的依賴項時才需要這樣做。MANIFEST.MF應該還有一個問題需要解決。雖然 com..client.api 包匯入了 com.google.gwt 包,但該匯入對於該包是私有的。任何匯入 com..client.api 包的包都不繼承此依賴項——必須明確指定。因此,在中,為 com.google.gwt 包新增一個Import-BundleStockPriceServiceImpl.java並儲存。如果仍然在
中看到問題,可能需要嘗試對其進行假編輯,否則您就做錯了什麼。
太棒了!現在我們已經完成了一半的共享服務建立工作。它目前應該看起來像這樣下一步是將 StockPriceServiceLondon 轉換為 OSGi 服務。我們將使用 Spring Dynamic Modules 來完成。Spring Dynamic Module 的原理很簡單——在/META-INF/spring
資料夾中提供 Spring 配置檔案,部署包時會為您建立一個 ApplicationContext。這是一種極好且非常簡單的方式,可以將 Spring 管理的 bean 匯出為 OSGi 服務。因此,在/META-INF中建立/spring中建立資料夾(右鍵單擊因此,在並選擇 新建 -> 資料夾)。然後右鍵單擊新的資料夾,選擇 新建 -> Spring Bean Definition。可以隨意命名,但在我的示例中命名為serviceLondon-config.xml。輸入名稱並單擊完成。現在需要將StockPriceServiceImpl
<bean id="stockPrices" class="com.google.gwt.sample.stockwatcher.server.StockPriceServiceImpl"/>
定義為 Spring bean,以便在部署包時建立它的一個例項。如果熟悉 Spring,這應該很容易。如果不熟悉,需要插入以下 XML
<osgi:service interface="com.google.gwt.sample.stockwatcher.client.api.StockPriceService" ref="stockPrices"/>
最後,需要將此 bean 匯出為 OSGi 服務。可以在同一個配置檔案中完成,但將 OSGi 依賴項分開是良好的實踐。因此建立另一個 Spring Bean Definition,命名為 osgi-config.xml,這次單擊下一步並勾選 osgi 名稱空間複選框,然後單擊完成。要將 bean 匯出為 OSGi 服務,請插入以下 XML
快速而粗糙的方法是臨時建立一個另一個 Spring Dynamic Module,它將消費該服務,將其注入到測試類中,然後只記錄輸出。這是確保服務工作正常的好方法,但顯然它沒有斷言任何內容,也無法在測試框架中執行。下一步是將 StockPriceServiceLondon 轉換為 OSGi 服務。我們將使用 Spring Dynamic Modules 來完成。Spring Dynamic Module 的原理很簡單——在我們已經構建了一個 Spring Dynamic Module,因此對於這個新的 TestConsumer 模組,我只強調差異。模組需要匯入 com.google.gwt.sample.stockwatcher.client.api 包(或 package),並且需要一些 Java 程式碼來呼叫服務客戶端。在
資料夾中需要 2 個 spring 配置檔案一個用於測試 bean(例如)
<bean id="consumer" class="com.ben.consumer.Consumer"> <constructor-arg ref="priceService"/> </bean>
testConsumer-config.xml),一個用於消費服務的<osgi:reference(例如)
<osgi:reference id="priceService" interface="com.google.gwt.sample.stockwatcher.client.api.StockPriceService"/>
osgi-config.xml在 Java 程式碼中,只需呼叫service.getPrices(new String{"foo", "bar"})StockPrice並傳入一些任意字串,應該返回一個StockPrice物件的陣列。如果想將輸出記錄到System.err.
,這會出現在 dm Server 的跟蹤目錄中<dm server install directory>/servicability/trace/<name of your test bundle>/trace.log要在 STS 中執行快速而粗糙的測試,需要啟動 dm Server。然後,將整個 StockWatcherServiceAPI 專案拖放到伺服器上。專案成功部署後,應該會收到類似如下的控制檯訊息
Deployment of 'com.google.gwt.sample.stockwatcher.client.api' version '1' completed。接下來,將整個 StockPriceServiceLondon 專案拖放到伺服器上,並等待部署訊息。如果這一切都初始化成功,將您的測試專案放到伺服器上。如果您想讓伺服器每次都乾淨啟動(清除舊的跟蹤輸出等),請在啟動配置中新增
-Dcom.springsource.server.clean=true
因此,開箱即用時,測試期望從本地 Maven 倉庫獲取其所有依賴項。可以配置測試使用其他型別的依賴管理,但為了簡單起見(嘿!),我正在使用 Maven。首先,我安裝了 m2eclipse,它透過新增http://m2eclipse.sonatype.org/update/作為更新站點(Help->Software Updates->Available Sites->Add Site)獲得。建議取消選擇可選的 Maven POM Editor,否則會收到一堆未滿足的依賴項警告,並且無法安裝。
建立一個新的 Java 專案,命名為 StockWatcherServiceTest(以src/main/javasrc作為原始檔夾)。右鍵單擊專案,選擇 Maven->Enable Dependency Management 使專案支援 Maven。接下來,建立一個新包(在我的例子中是com.ben),並將 Spring DM 參考指南中的SimpleOSGiTest
程式碼複製到該包中的測試類(請注意,測試框架不允許使用預設包)。這個測試類將幫助我們進行健全性檢查,確認依賴項都已正確設定。會注意到測試尚無法編譯,因為我們尚未擁有它所需的依賴項。需要在pom.xml會注意到測試尚無法編譯,因為我們尚未擁有它所需的依賴項。需要在檔案中指定正確的依賴項。為了節省您數小時的捶胸頓足和詛咒,您可以在此處檢視我的
pom.xml檔案(包含在完整的工作空間下載中)。一旦依賴項工作正常,看看能否找出示例程式碼中的拼寫錯誤,新增 Java import,現在應該有一個可以編譯的測試類了。嘗試執行測試:右鍵單擊 -> 執行方式 -> JUnit Test。如果收到ClassNotFoundException,是因為測試在錯誤的位置查詢.class檔案。解決此問題的最簡單方法是覆蓋getRootPath()並返回
"file:./target/classes"
或 .class 檔案生成的任何其他路徑。希望現在會看到綠條,這至少證明了測試用例啟動了 Equinox 框架併成功建立了即時包。現在可以為 StockWatcherServiceLondon 包建立合適的單元測試了。下一步是使測試依賴包正確載入和解析。為此,必需的包需要安裝到本地 maven 倉庫,並在測試用例中的
getTestBundlesNames()會注意到測試尚無法編譯,因為我們尚未擁有它所需的依賴項。需要在方法中指定。會注意到測試尚無法編譯,因為我們尚未擁有它所需的依賴項。需要在先處理 Maven:之前建立的MANIFEST.MFpom.xml檔案應該已經下載了外部依賴項,但我們需要手動將 com..client.api、com..service.london 和 com.google.gwt 包新增到 maven 倉庫。我透過對工作空間中的專案進行 Maven 化併為每個專案建立一個pom.xml會注意到測試尚無法編譯,因為我們尚未擁有它所需的依賴項。需要在檔案來完成此操作。這裡有幾個陷阱。首先,要讓 Maven 識別會注意到測試尚無法編譯,因為我們尚未擁有它所需的依賴項。需要在 manifest中,為 com.google.gwt 包新增一個檔案,必須在pom.xml中配置maven-jar-plugin並指向檔案位置。在此處檢視我的pom.xml。其次,我發現 Spring dm Server 專有的
manifest希望現在會看到綠條,這至少證明了測試用例啟動了 Equinox 框架併成功建立了即時包。現在可以為 StockWatcherServiceLondon 包建立合適的單元測試了。條目不起作用,所以我必須修改兩個專案,轉而使用多個
Import-Package檔案(包含在完整的工作空間下載中)。一旦依賴項工作正常,看看能否找出示例程式碼中的拼寫錯誤,新增 Java import,現在應該有一個可以編譯的測試類了。條目。進行這些更改後,開啟命令提示符,切換到專案目錄並輸入mvn install。這將構建外掛並安裝到本地 maven 倉庫。要安裝我們在第 2 部分構建的 gwt 外掛,我使用了以下命令列mvn install:install-file -DgroupId=com.google.gwt -DartifactId=com.google.gwt -Dversion=1.5.3 -Dpackaging=maven-plugin -Dfile=<location of jar file>.
。此時值得檢查一下外掛是否正確構建,可以透過解壓倉庫中的 jar 檔案並檢查清單檔案是否正常來完成。),一個用於消費服務的一旦所有測試包構建並安裝到倉庫中,需要在測試用例的(例如method中指定它們。語法在參考文件中描述,我指定了 4 個包:com..client.api、com..service.london、com.google.gwt 和 javax.servlet。希望此時,您將能夠在沒有任何實際斷言的情況下執行測試,並且應該能夠成功地安裝和啟動所有測試包而沒有問題。如果不起作用,一個有用的除錯工具是啟用 Equinox telnet 控制檯,並用它來檢查預期的匯入和匯出看起來是否都正確。如果收到因此,在UnsatisfiedLinkError
,它們可能不正確。啟用控制檯的方法是覆蓋createPlatform()並呼叫
System.setProperty("osgi.console", "9000")
getConfigLocations()
方法,該方法返回一個配置檔名的 String 陣列。在這種特殊情況下,我們將使其成為一個普通的 OSGi 包,而不是 Spring Dynamic Module,因此無需將檔案放到),一個用於消費服務的/META-INF/spring中建立資料夾中。(例如在整合測試中,Spring 將自動裝配所有 Spring 管理的 bean 到存在適當 setter 方法的測試用例中,因此新增),一個用於消費服務的setStockPriceService(StockPriceService s)因此,在方法將把 OSGi 服務注入到測試類中。一旦獲得了服務的引用,就可以開始對其進行斷言了……終於!現在,我意識到這是一個非常冗長的描述,而最終只是一個簡單的概念。坦白地說,理解配置的最佳方法是檢視我的 Eclipse 工作空間此處。我詳細描述它是因為看到一個工作示例是一回事,而理解實現過程和陷阱是另一回事。然而,一旦設定好,這是一種極其強大的方法,可以以自動化方式整合測試包服務。步驟 4:消費共享服務
在步驟 2 中,我們建立了一個 API 包和一個共享服務包。現在需要重構我們的其他專案以在編譯時和執行時與這些包一起工作。在步驟 2 結束時,您將破壞 StockWatcherWar 和 StockWatcher 專案中的一些依賴項。先修復這些。
<context-param> <param-name>contextClass</param-name> <param-value>com.springsource.server.web.dm.ServerOsgiBundleXmlWebApplicationContext</param-value> </context-param> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/META-INF/*.xml</param-value> </context-param>
從 StockWatcher 開始,它損壞了,因為我們移除了它所依賴的 WAR 專案中的 API。更改其構建時依賴項,使其引用 StockWatcherServiceAPI 專案(右鍵單擊 -> 構建路徑 -> 配置構建路徑 -> 專案)。此專案沒有執行時依賴項,因此需要做的就是這些。
StockWatcherWar 現在需要進行一些修改來消費我們在步驟 2 中建立的 OSGi 服務。當前,它仍然包含我們在步驟 2 中複製到服務包中的服務程式碼,所以一項工作是移除此程式碼,並用一個轉而委託給 OSGi 服務的呼叫來替換它。然而,這意味著我們需要以某種方式將服務包的引用傳遞到類中……這也意味著我們實際上需要從某個地方獲取它的一個例項。讓我們進一步分析。。輸入名稱並單擊完成。現在需要將好的,我們已經知道如何獲取服務的引用,因為我們在上面的測試方法中做到了:建立一個 spring 配置檔案並新增一個<osgi:referencepom.xml標籤。所以我們先做這個。在/META-INF中建立一個新的 Spring Bean Definition(右鍵單擊 -> 新建 -> Spring Bean Definition),隨意命名(例如osgi-service-references.xml),並確保勾選 osgi 名稱空間複選框以包含 schema。新增步驟 3 中的<osgi:reference
示例程式碼,現在您有了一個用於消費 OSGi 服務的 Spring 配置檔案。這個配置檔案是否應該放到。輸入名稱並單擊完成。現在需要將/META-INF/spring子目錄中,使其成為 Spring Dynamic Module?正確答案是否定的。我們需要用它來引導一個特定型別的 ApplicationContext,該 ApplicationContext 在web.xml中定義,因此無需為我們自動建立另一個。.
順便說一下,編輯 web.xml 並新增以下行。輸入名稱並單擊完成。現在需要將這將引導一個 dm Server ApplicationContext,並使用 /META-INF 中的所有 xml 檔案來配置它。中,為 com.google.gwt 包新增一個接下來,我們需要弄清楚如何將這個 Spring 管理的服務例項傳遞給
StockPriceServiceImpl),並確保勾選 osgi 名稱空間複選框以包含 schema。新增步驟 3 中的類,換句話說,一種連線 Spring 和 servlet 世界的方法。一種方法是從中建立ServletContext查詢ApplicationContext並呼叫其):
<bean id="myFilter" class="com.google.gwt.sample.stockwatcher.server.StockPriceServiceFilter"> <constructor-arg ref="priceService"/> </bean>
getBean()方法。這不太好,因為它要求我們將 bean 名稱硬編碼到服務程式碼中,並依賴於依賴查詢。一種更清晰的機制是使用 Spring 管理的ServletFilter),並確保勾選 osgi 名稱空間複選框以包含 schema。新增步驟 3 中的,將服務例項注入其中,然後使用靜態常量進行查詢,將其傳遞給 servlet。這就是我在這裡實現的現在,我意識到這是一個非常冗長的描述,而最終只是一個簡單的概念。坦白地說,理解配置的最佳方法是檢視我的 Eclipse 工作空間此處。我詳細描述它是因為看到一個工作示例是一回事,而理解實現過程和陷阱是另一回事。然而,一旦設定好,這是一種極其強大的方法,可以以自動化方式整合測試包服務。這個類應該和現在,我意識到這是一個非常冗長的描述,而最終只是一個簡單的概念。坦白地說,理解配置的最佳方法是檢視我的 Eclipse 工作空間此處。我詳細描述它是因為看到一個工作示例是一回事,而理解實現過程和陷阱是另一回事。然而,一旦設定好,這是一種極其強大的方法,可以以自動化方式整合測試包服務。StockPriceServiceImpl),並確保勾選 osgi 名稱空間複選框以包含 schema。新增步驟 3 中的類一起建立在 StockWatcherWar 專案中。另外,別忘了在doFilter())
<filter> <filter-name>myFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>myFilter</filter-name> <servlet-name>StockService</servlet-name> </filter-mapping>
中委託給方法。這不太好,因為它要求我們將 bean 名稱硬編碼到服務程式碼中,並依賴於依賴查詢。一種更清晰的機制是使用 Spring 管理的FilterChain現在,我意識到這是一個非常冗長的描述,而最終只是一個簡單的概念。坦白地說,理解配置的最佳方法是檢視我的 Eclipse 工作空間此處。我詳細描述它是因為看到一個工作示例是一回事,而理解實現過程和陷阱是另一回事。然而,一旦設定好,這是一種極其強大的方法,可以以自動化方式整合測試包服務。。如果您一直仔細按照說明操作,由於步驟 1 中的重構,這個類和
StockPriceServiceImpl。輸入名稱並單擊完成。現在需要將都無法編譯。要修復這個問題,需要將 StockWatcherServiceAPI 新增為 StockWatcherWar 的專案引用(右鍵單擊 -> 屬性 -> 專案引用),然後將 com.google.gwt.sample.stockwatcher.client.api 新增到
Import-Package
列表中。進行此更改後,程式碼現在應該可以編譯了。
現在有了 servlet 和 ApplicationContext 之間的橋樑,我們需要做的就是建立GwtRpcStockServiceFilter
的例項,並將服務注入其中。在/META-INF中建立另一個 spring bean 定義,隨意命名(例如stockwatcher-config.xml)。將以下配置複製到其中(假設您使用的 OSGi 服務的 bean 名稱是中,為 com.google.gwt 包新增一個priceService
)。
,並在
- MANIFEST.MFweb.xml資料夾,選擇 新建 -> Spring Bean Definition。可以隨意命名,但在我的示例中命名為中定義。將此程式碼複製到您的StockPriceServiceImpl.javaweb.xml
中(它假設您的
filter
filter
對映到了 StockService servlet。web.xml 配置現在完成了。
確定了部署模式後,您可以使用管理控制檯上傳檔案,或者將檔案複製到<dm Server 安裝目錄>/pickup目錄中。首次執行此操作時,伺服器需要已處於執行狀態,並且您應按正確的順序將 bundle 複製到目錄中。dm Server 會記住此順序,以便您下次啟動伺服器時使用。使用此機制的優勢在於,只需將單個 bundle 移入或移出此目錄,即可對其進行熱部署或取消部署。