2010-01-26 42 views
0

在我當前的項目中,我正在處理實現巨大接口的EJB。 實現是通過業務委託來完成的,該業務委託實現相同的接口幷包含真實的業務代碼。Java + Command Pattern + Spring + remoting:如何向Command對象注入依賴項?

這個 '命令模式' 的使用順序建議的一些文章是

  1. 客戶端創建一個命令和參數它
  2. 客戶端發送命令到服務器
  3. 服務器接收命令,日誌,審計和斷言命令可以送達
  4. 服務器上執行命令
  5. 服務器返回命令結果的客戶

的問題發生在步驟4 .:

現在我正在使用spring context從命令中的上下文獲取bean,但是我想將依賴項注入命令。

這是一個天真的用法說明的目的。我已經添加評論我哪裏有問題:

public class SaladCommand implements Command<Salad> {  
    String request; 

    public SaladBarCommand(String request) {this.request = request;} 

    public Salad execute() {  
     //this server side service is hidden from client, and I want to inject it instead of retrieving it 
     SaladBarService saladBarService = SpringServerContext.getBean("saladBarService");  
     Salad salad = saladBarService.prepareSalad(request);  
     return salad; 
    } 
} 

public class SandwichCommand implements Command<Sandwich> {  
    String request; 

    public SandwichCommand(String request) {this.request = request;} 

    public Sandwich execute() { 
     //this server side service is hidden from client, and I want to inject it instead of retrieving it  
     SandwichService sandwichService = SpringServerContext.getBean("sandwichService");  
     Sandwich sandwich = sandwichService.prepareSandwich(request);  
     return sandwich; 
    } 
} 

public class HungryClient { 
    public static void main(String[] args) { 
     RestaurantService restaurantService = SpringClientContext.getBean("restaurantService"); 
     Salad salad = restaurantService.execute(new SaladBarCommand(
      "chicken, tomato, cheese" 
     )); 
     eat(salad); 

     Sandwich sandwich = restaurantService.execute(new SandwichCommand(
      "bacon, lettuce, tomato" 
     )); 
     eat(sandwich); 
    } 
} 

public class RestaurantService { 
    public <T> execute(Command<T> command) { 
     return command.execute(); 
    } 
} 

我想擺脫調用像SandwichService sandwichService = SpringServerContext.getBean("sandwichService"); ,並已注入我的服務,而不是。

如何做到最簡單的方法?

+1

這個問題與EJB有什麼關係,真的嗎?看起來你只是問如何在Spring中連接事情。什麼是'SampleCommand'?誰實例化它?誰使用它?它是什麼? – skaffman 2010-01-26 15:24:02

+0

我已經更新了我的問題,以便在skaffman發表評論後作出澄清。 – Guillaume 2010-01-26 16:31:24

+0

如果你曾經爲這個問題實施過某些東西,我很樂意聽到它。我遇到與執行客戶端不需要關心的命令所需的服務端服務相同的問題。 – Jaapjan 2011-10-19 13:57:22

回答

0

如果將SimpleCommand通過Spring ApplicationContext注入到使用它的類中(實際上它應該是),那麼您簡單地需要將它的依賴項表示爲構造函數參數或setter並注入它們。

很難給不認識任何更多的細節誰在使用SimpleCommand,它來自哪裏,等等

+0

SampleCommand只是一個示例...它是一個虛構的命令。 命令在「客戶端」中實例化併發送到「服務器」。所以我們在創建命令時不能使用Spring DI:依賴關係是服務器端。 – Guillaume 2010-01-26 15:52:45

1

我已經建立在過去的驚人相似的東西,但我們好像你是沒有用的命令模式正在做。在你的情況下,你的命令似乎什麼都不做,只是實際查找並運行一個服務方法,所以爲什麼不簡單地將該服務方法作爲API呈現,而不是完全使用命令模式。然後,您可以通過Spring Remoting將服務調用連接到EJB,並且所有Spring細節都可以保留在特定於協議的層(Servlet,EJB,MDB ...)中,並且代碼對其周圍發生的事情保持奇妙無知。

我們的基礎設施看起來像這樣。 (對於那些會抱怨EJB存在的人來說,這不是整個基礎架構,並且出於安全性和性能方面的原因,我們使用EJB到EJB調用服務來進行服務交互)。

Eclipse富客戶端 - >(春季遠程處理 - HTTP) - >的Servlet - >(本地接口) - > EJB - >服務實現

servlet的 - 使用Spring上下文來查找本地EJB接口並使用RemoteInvocation對象(由Spring Remoting中的HttpProxyFactoryBean生成併發送)和服務接口的名稱調用通用EJB接口的通用invoke方法。

的EJB - 中查找基於其接口名稱服務(也bean的名稱),並使用一個RemoteInvocationExecutor來調用與RemoteInvocation對象服務實現的方法。

現在EJB可以綁定到多個服務(儘管我們使用一對一的部署模型)。您可以將Spring Remoting用於基於Http,EJB或JMS的對來自不同應用程序的服務的調用。因爲您只需將測試直接連接到實施,所以在沒有服務器部署的情況下進行測試是微不足道的。

注意:如果我有機會,我會嘗試添加一些代碼片段。

+0

Well命令可以執行的不僅僅是調用服務方法:有些可能更復雜,我可以將它們鏈接起來。我想用命令替換怪物EJB中越來越多的方法(有時非常相似)。命令可以被看作'遠程回調'。我不是很想理解「將這種服務方法當做API」,但對我來說聽起來像是我試圖擺脫的巨大服務界面...... – Guillaume 2010-01-26 16:14:37

+1

我不認爲這是一個分佈式行爲的可行模型。你想在客戶端創建一個Command對象,只有數據,但是當它在服務器端重新構建時,會注入該Command的依賴關係。要做到這一點,您應該在服務器端構建一個Command裝飾器,將客戶端生成的Command作爲依賴項,以及您擁有的任何其他依賴項。然後,通用EJB將通過Spring獲得裝飾器(服務已注入),並在調用execute方法之前注入Command對象。 – Robin 2010-01-26 18:18:01

+1

它仍然聽起來像簡化你的「大界面」,分解它,提供其他組合的服務,比如更簡單(更簡潔)。然後只需使用這些新界面即可隱藏或替換舊界面。這很好地將您的業務數據與業務服務分離,這正是您想要的遠程服務。將數據和操作組合在同一個對象中並不適合網絡服務調用。 – Robin 2010-01-26 18:23:38

0
public class SampleCommand implements Command {  
    private final String parameter; 
    private final ServiceBean service; 

    //the client build the command using a parameter 
    public SampleCommand(ServiceBean service, String parameter) { 
     this.parameter = parameter; 
     this.service = service; 
    } 

    //this code will be executed by the server 
    public Object execute() { 
     //do something using the parameter and return the result 
     return service.doSomethingWith(parameter);    
    } 
} 

您可以注入一個服務帶或不帶彈簧:

<bean id="sampleCommand" class="package.SampleCommand"> 
    <constructor-arg ref="serviceBean" /> 
    <constructor-arg value="Param" /> 
</bean> 

一些調用的應用程序:

ServiceBean service = ServiceProxy.getService(SampleCommand.class); 
Command command = new SampleCommand(service, "Param"); 

依賴注入是不依賴於一個框架。

+0

這不是理解DI如何與Spring或PicoContainer一起工作的問題,它是關於如何在組件在網絡中的其他地方實例化時提供DI。 – Guillaume 2010-01-26 16:42:28

+0

我認爲你可能會過度設計一些東西。也許看看戰略模式。服務應該消耗一個命令,而不是相反。 – Droo 2010-01-26 23:19:08

+0

如果命令需要DAO來完成它的工作,該怎麼辦?或外部服務? – Guillaume 2011-06-15 19:10:29

2

這不是認識 問題DI是如何工作的

我紀堯姆同意,因爲我滿足這一相同的情況下。

主要問題是實例和對象生命週期。

  1. 該命令可以創建多個時間是相同的方法,例如。 並以編程方式。

  2. 春,集裝箱,假設你想用示波器(會話,原型=螺紋範圍),應用範圍一旦創建對象...

所以命令不通過創建春天,它應該使用春天創建的服務。 但該服務沒有注入命令。

謝謝

3

您可以使用Spring的@Configurable註解,讓AspectJ來爲Spring Bean注入到未由Spring創建的對象。看看here

2

在服務器端,在RestaurantService,請執行下列操作:

  1. 實現ApplicationContextAware接口 - 這將導致春季注入到應用程序上下文到RestaurantService參考。

  2. 在RestaurantService.execute(命令)的方法,這樣做:

    AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory(); 
    beanFactory.autowireBeanProperties(command, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true); 
    command.execute(); 
    
  3. 最後,在你的應用環境,宣佈每次您的命令對象的實例,該注入的依賴。

這應該允許您在客戶端上創建對象,序列化它們,將它們發送到服務器,並在使用之前必須依賴注入的效果。當您收到物品時,而不是在您使用物品之前進行注射可能會更清潔。

還有一些關於如何使用AutowireCapableBeanFactory的選項 - 該示例使用適當的類找到一個bean定義並設置在app上下文中定義的屬性。如果您可以將每個Command實現與應用上下文中的名稱相關聯,則可以使用configureBean(Object,String)來代替支持回調。

+0

這是我正在使用的解決方案。我非常喜歡它。 – 2013-09-16 21:33:48