使用 Guice 進行依賴注入 - Play 2.4 2.5

Guice 是 Play 的預設依賴注入(進一步的 DI )框架。其他框架也可以使用,但使用 Guice 可以使開發工作更容易,因為 Play 會在面紗下處理事情。

注入 Play API-s

從 Play 2.5 開始,應該使用 DI 建立幾個 API-s,這些 API 在早期版本中是靜態的。例如,這些是 ConfigurationJPAApiCacheApi 等。

Play API 的注入方法對於類是不同的,該類由 Play 和自定義類自動注入。在自動注入的類中注入就像在欄位或建構函式上放置適當的 @Inject 註釋一樣簡單。例如,要在具有屬性注入的控制器中注入 Configuration

@Inject 
private Configuration configuration;

或者使用建構函式注入:

private Configuration configuration;
@Inject
public MyController(Configuration configuration) {
    this.configuration = configuration;
}

註冊 DI自定義類中的注入應該像自動注入類一樣完成 - 使用 @Inject 註釋。

來自自定義類的注入( 不受 DI 限制 )應該通過使用 Play.current()。injector() 顯式呼叫注入器來完成。例如,要在自定義類中注入 Configuration ,請定義一個配置資料成員,如下所示:

private Configuration configuration = Play.current().injector().instanceOf(Configuration.class);

自定義注入繫結

自定義注入繫結可以使用 @ImplementedBy 註釋完成,也可以使用 Guice 模組以程式設計方式完成。

用 @ImplementedBy 註釋注入

使用 @ImplementedBy 註釋注入是最簡單的方法。下面的示例顯示了一個服務,它為快取提供了一個外觀。

  1. 該服務由介面 CacheProvider 定義如下:

    @ImplementedBy(RunTimeCacheProvider.class)
    public interface CacheProvider {  
       CacheApi getCache();  
    }  
    
  2. 該服務由 RunTimeCacheProvider 類實現:

    public class RunTimeCacheProvider implements CacheProvider {  
       @Inject  
       private CacheApi appCache;  
       @Override  
       public public CacheApi getCache() {  
         return appCache;  
       }  
    }  
    

注意 : 在建立 RunTimeCacheProvider 例項時會注入 appCache 資料成員。 **

  1. 快取檢查器被定義為具有 @Inject 批註的控制器的成員,並從控制器呼叫:
public class HomeController extends Controller {
  @Inject
  private CacheProvider cacheProvider;
  ...      
  public Result getCacheData() {
        Object cacheData = cacheProvider.getCache().get("DEMO-KEY");
        return ok(String.format("Cache content:%s", cacheData));  
  }      

使用 @ImplementedBy 註釋注入會建立固定繫結: 上面示例中的 CacheProvider 始終使用 RunTimeCacheProvider 進行例項化。當存在具有單個實現的介面時,這種方法僅適用於一種情況。對於具有多個實現的介面或者在沒有抽象介面的情況下實現為單例的類,它無能為力。老實說,@ ImplementedBy 將在極少數情況下使用。它更可能與 Guice 模組一起使用程式化繫結。

使用預設 Play 模組進行注入繫結

預設的 Play 模組是根專案目錄中名為 Module 的類,定義如下:

import com.google.inject.AbstractModule;  
public class Module extends AbstractModule {  
  @Override  
  protected void configure() {  
      // bindings are here           
  }  
}  

注意 :上面的程式碼段顯示了 configure 中的繫結,但當然任何其他繫結方法都會得到遵守。

對於 CacheProviderRunTimeCacheProvider 的程式設計繫結 :

  1. CacheProvider 的定義中刪除 @ImplementedBy 註釋 : **
public interface CacheProvider {  
  CacheApi getCache();  
}  
  1. 實施模組配置如下:
public class Module extends AbstractModule {  
  @Override  
  protected void configure() {  
    bind(CacheProvider.class).to(RunTimeCacheProvider.class);
  }  
}  

靈活的注入繫結與預設播放模組

RunTimeCacheProvider 在使用假應用程式的 JUnit 測試中不能很好地工作 (請參閱單元測試主題)。因此,單元測試需要不同的 CacheProvider 實現。注入粘合應根據環境進行。

我們來看一個例子吧。

  1. FakeCache 提供了在執行測試時使用的 CacheApi 的存根實現 (它的實現不是那麼有趣 - 它只是一個對映)。
  2. FakeCacheProvider 實現在執行測試時使用的 CacheProvider
public class FakeCacheProvider implements CacheProvider {  
  private final CacheApi fakeCache = new FakeCache(); 
  @Override  
  public CacheApi getCache() {  
    return fakeCache;
  }  
}  
  1. 模組實現如下:
public class Module extends AbstractModule { 
  private final Environment environment;
  public Module(Environment environment, Configuration configuration) {
    this.environment = environment;
  }
  @Override  
  protected void configure() {  
     if (environment.isTest() ) {
      bind(CacheProvider.class).to(FakeCacheProvider.class);
    }
    else {
      bind(CacheProvider.class).to(RuntimeCacheProvider.class);   
    }
  }  
}  

這個例子僅用於教育目的。對模組內部的測試進行繫結不是最佳實踐,因為這會在應用程式和測試之間進行耦合。測試的繫結應該由測試本身完成,模組不應該知道測試特定的實現。瞭解如何在…中做得更好。

使用自定義模組進行注入繫結

自定義模組與預設的 Play 模組非常相似。不同之處在於它可能有任何名稱,屬於任何包裝。例如,模組 OnStartupModule 屬於包模組。

package modules;  
import com.google.inject.AbstractModule;  
public class OnStartupModule extends AbstractModule {  
   @Override  
   protected void configure() {  
       ...            
   }  
}  

應該通過 Play 顯式啟用自定義模組以進行呼叫。對於模組 OnStartupModule ,應將以下內容新增到 application.conf 中

play.modules.enabled += "modules.OnStartupModule"