Play具有用于整合Guice的模塊:
http://www.playframework.org/modules/guice-1.2/home
除了模塊文檔之外, @ _ felipera的這篇文章還可以幫助您入門。
http://geeks.aretotally.in/dependency-injection-with-play-framework-and-google-guice
如何使用Guice模塊
添加依賴項
require:- play- play -> guice 1.2
下載依賴項
play deps
創建一個將注入控制器的新類
services.MyService
package services;
public interface MyService {public void sayHello();
}
services.MyServiceImpl
package services;
public class MyServiceImpl implements MyService {public MyServiceImpl(){play.Logger.info("constructor!");}@Overridepublic void sayHello() {play.Logger.info("hello");}
}
配置進樣器
package config;
public class GuiceConfig extends GuiceSupport {@Overrideprotected Injector configure() {return Guice.createInjector(new AbstractModule() {@Overrideprotected void configure() {bind(MyService.class).to(MyServiceImpl.class).in(Singleton.class);}});}
}
這會將類設置為單例。 每次類具有MyService的依賴項時,注入程序都會注入MyServiceImpl的相同實例。
使用@Inject批注注入依賴項
package controllers;
public class Application extends Controller {@Injectstatic MyService myService;public static void index() {myService.sayHello();render();}
}
測試中
我的下一步是創建測試,這是第一個問題
play test
http:// localhost:9000 / @ tests
編譯錯誤! 問題在于該模塊有一個名為“ test”的文件夾。 該文件夾應該進行一些單元測試或功能測試,但是它具有三個示例應用程序。 播放模塊中的約定是在“ samples and-test”文件夾中具有此類應用程序。
我在項目的分支上重命名了此文件夾:
https://github.com/axelhzf/play-guice-module
我也做了請求請求,但到目前為止我沒有得到任何回應: https://github.com/pk11/play-guice-module/pull/5 重命名文件夾“ test”足以運行此測試:
@InjectSupport
public class InjectTest extends UnitTest {@Injectstatic MyService myService;@Testpublic void injectOk(){assertNotNull(myService);}
}
添加更多依賴
默認情況下,Play自動檢測類上的@Inject注釋,而不是繼承自Controller,Job和Mail。 如果要注入對其他類的依賴項,則必須使用@InjectSupport。 通常,我們的服務并不像MyService那樣簡單。 服務之間具有依賴關系是很常見的。 Guice解決了這個問題,分析了依存關系并以正確的順序實例化了對象。 services.MyDependentService
package services;public interface MyDependentService {public void sayHelloWorld();
}
service.MyDependentServiceImpl
package services;@InjectSupport
public class MyDependentServiceImpl implements MyDependentService {@Injectstatic MyService myService;public MyDependentServiceImpl(){play.Logger.info("Init MyDependentServiceImpl");}public void sayHelloWorld(){myService.sayHello();play.Logger.info("world");}
}
注入測試
@InjectSupport
public class InjectTest extends UnitTest {@Inject
static MyDependentService myDependentService;@Test
public void injectOk(){assertNotNull(myDependentService);myDependentService.sayHelloWorld();
}}
在GuiceConfig中綁定
bind(MyDependentService.class).to(MyDependentServiceImpl.class).in(Singleton.class);
這是控制臺輸出
20:34:39,090 INFO ~ Init MyServiceImpl
20:34:39,095 INFO ~ Init MyDependentServiceImpl
20:34:39,095 INFO ~ Application 'lazySingleton' is now started !
20:34:39,136 INFO ~ hello
20:34:39,136 INFO ~ world
構造器注入
我對模塊不滿意的一件事是,允許您注入的字段必須是靜態的。 我寧愿將依賴項聲明為構造函數參數。 這樣,很明顯,要創建MyDependentServiceImpl實例,您需要一個MyService實例。 而且,在進行單元測試時,將模擬對象作為參數傳遞比配置注入器更容易。 在模塊文檔中,我沒有找到有關如何執行此操作的參考。 不過,我發現了一篇文章,解釋了如何使用提供程序來執行此操作: http://ericlefevre.net/wordpress/2011/05/08/play-framework-and-guice-use-providers-in-guice-modules/ 后來,我在StackOverflow上發現了一個問題,這給了我另一個線索: http://stackoverflow.com/questions/8435686/does-injector-getinstance-always-call-a-constructor 他在Edit中說,他忘記將@Inject注釋放入構造函數中。 我試圖做同樣的事情,終于成功了:
public class MyDependentServiceImpl implements MyDependentService {private final MyService myService;@Injectpublic MyDependentServiceImpl(MyService myService){this.myService = myService;play.Logger.info("Inicializando MyDependentServiceImpl");}...
懶惰的單身人士
擁有完善的google guice配置仍然是最后一個細節。
在應用程序啟動時初始化服務。
21:38:11,801 INFO ~ Inicializando MyServiceImpl
21:38:11,805 INFO ~ Inicializando MyDependentServiceImpl
21:38:11,805 INFO ~ Application 'lazySingleton' is now started !
當應用程序處于生產模式時,這是正確的行為。 但是在開發模式下,我更喜歡按需初始化Singleton。 可能有一些服務需要花一些時間才能啟動,因此我希望應用程序盡快啟動。
使用Google Guice,您可以使用Scopes實現此目的:
http://code.google.com/p/google-guice/wiki/范圍
您要做的就是設置Stage參數:
Stage stage = Play.mode.isDev() ? Stage.DEVELOPMENT : Stage.PRODUCTION;
return Guice.createInjector(stage, new AbstractModule() {…..
重新運行測試
22:00:03,353 WARN ~ You're running Play! in DEV mode
22:00:04,615 INFO ~ Connected to jdbc:h2:mem:play;MODE=MYSQL;LOCK_MODE=0
22:00:04,811 INFO ~ Guice injector created: config.GuiceConfig
22:00:04,819 INFO ~ Init MyServiceImpl
22:00:04,824 INFO ~ Init MyDependentServiceImpl
22:00:04,824 INFO ~ Application 'lazySingleton' is now started !
哎呀! 在應用程序啟動之前初始化Singleton。 也許那不是stage變量的正確用法。 讓我們嘗試一下測試:
public class StageTest {@Testpublic void testDevelopment(){Injector injector = createInjector(Stage.DEVELOPMENT);System.out.println("development - before getInstance");MyService instance = injector.getInstance(MyService.class);System.out.println("development - after getInstance");}@Testpublic void testProduction(){Injector injector = createInjector(Stage.PRODUCTION);System.out.println("production - before getInstance");MyService instance = injector.getInstance(MyService.class);System.out.println("production - after getInstance");}public Injector createInjector(Stage stage){Injector injector = Guice.createInjector(stage, new AbstractModule(){@Overrideprotected void configure() {bind(MyService.class).to(MyServiceImpl.class);}});return injector;}
}
結果是:
INFO: development - before getInstance
INFO: Inicializando MyServiceImpl
INFO: development - after getInstanceINFO: Inicializando MyServiceImpl
INFO: production - before getInstance
INFO: production - after getInstance
就像文檔中說的那樣,在開發模式下,Singleton被延遲初始化。
如果這行得通,當我嘗試使用播放模塊時,為什么不行?
查看代碼:
https://github.com/pk11/play-guice-module/blob/master/src/play/modules/guice/GuicePlugin.java
@OnApplicationStart該模塊查找所有帶有@InjectSupport注釋的類,并注入其依賴項。 要注入依賴項,模塊調用getBean()方法。 在這里,我們發現了問題:調用getBean()時,該類被實例化。
我找到了解決此問題的方法:
https://groups.google.com/d/msg/google-guice/405HVgnCzsQ/fBUuueP6NfsJ
這是代碼:
- @LazySingleton
- 更多范圍
- 懶人
這些類為每個標記為@LazySingleton的類創建一個代理。 當注入對象時,注入器實際上會注入代理。 第一次調用方法時,代理將負責初始化類。
使用這些類,注入器配置如下所示:
public class GuiceConfig extends GuiceSupport {@Overrideprotected Injector configure() {Stage stage = Play.mode.isDev() ? Stage.DEVELOPMENT : Stage.PRODUCTION;return Guice.createInjector(stage, new AbstractModule() {@Overrideprotected void configure() {bindScope(LazySingleton.class, MoreScopes.LAZY_SINGLETON);bindLazySingletonOnDev(MyService.class, MyServiceImpl.class);bindLazySingletonOnDev(MyDependentService.class, MyDependentServiceImpl.class);}protected <T> void bindLazySingletonOnDev(Class<T> expected, Class<? extends T> implClass){if(Play.mode.isDev()){bind(implClass).in(MoreScopes.LAZY_SINGLETON);Provider<T> provider = LazyBinder.newLazyProvider(expected, implClass);bind(expected).toProvider(provider);}else{bind(expected).to(implClass).in(Scopes.SINGLETON);}}});}
}
我將把這些類添加到派生中,以便擁有可以在項目之間重用的完整模塊。
結論
在過去的幾年中,Dependency Injection已經從一個晦澀難懂的術語發展成為每個程序員最精干的一部分。 在本文中,我們看到了將Guice(來自Google的一個非常方便的庫)集成到Play框架應用程序有多么容易。 此外,我們還介紹了如何自定義行為,以獲得更好的開發體驗。
文章原文發表在http://axelhzf.tumblr.com上 。
參考:我們的JCG合作伙伴提供的 Playframework + Google Guice ? Sebastian Scarano在“ 玩轉Play框架”中! 博客。
翻譯自: https://www.javacodegeeks.com/2012/02/play-framework-google-guice.html