Compile time dependency injection in Play 2.4 with macwire
-
Upload
yanns -
Category
Technology
-
view
1.148 -
download
0
Transcript of Compile time dependency injection in Play 2.4 with macwire
![Page 1: Compile time dependency injection in Play 2.4 with macwire](https://reader030.fdocuments.us/reader030/viewer/2022032502/55b7ab96bb61eb105b8b4828/html5/thumbnails/1.jpg)
DI in Play 2.4Compile time
Dependency Injection
with macwire
Yann Simon
![Page 2: Compile time dependency injection in Play 2.4 with macwire](https://reader030.fdocuments.us/reader030/viewer/2022032502/55b7ab96bb61eb105b8b4828/html5/thumbnails/2.jpg)
Dependency Injection
RuntimeVS
Compile Time
![Page 3: Compile time dependency injection in Play 2.4 with macwire](https://reader030.fdocuments.us/reader030/viewer/2022032502/55b7ab96bb61eb105b8b4828/html5/thumbnails/3.jpg)
Runtime vs compile time DI
● Runtime✔ Support Lifecycle (start / stop)
● Compile time✔ Dependency graph checked by compiler✔ No runtime overhead
![Page 4: Compile time dependency injection in Play 2.4 with macwire](https://reader030.fdocuments.us/reader030/viewer/2022032502/55b7ab96bb61eb105b8b4828/html5/thumbnails/4.jpg)
source:http://apmblog.compuware.com/2013/12/18/the-hidden-class-loading-performance-impact-of-the-spring-framework/
![Page 5: Compile time dependency injection in Play 2.4 with macwire](https://reader030.fdocuments.us/reader030/viewer/2022032502/55b7ab96bb61eb105b8b4828/html5/thumbnails/5.jpg)
Compile time DI
● With the cake pattern– Talk “Structure your Play application with the cake
pattern (and test it)”● Slides: http://de.slideshare.net/yann_s/play-withcake-export2● Video: http://www.ustream.tv/recorded/42775808
● With constructor parameters
![Page 6: Compile time dependency injection in Play 2.4 with macwire](https://reader030.fdocuments.us/reader030/viewer/2022032502/55b7ab96bb61eb105b8b4828/html5/thumbnails/6.jpg)
DI with constructor parameters
class Dependency1
class Dependency2 { def parse(input: String): Unit = println(s"parse '$input' with '$this'")}
class Service(dep1: Dependency1, dep2: Dependency2) { def parse(input: String): Unit = dep2.parse(input)}
val dep1 = new Dependency1val dep2 = new Dependency2val service = new Service(dep1, dep2)
![Page 7: Compile time dependency injection in Play 2.4 with macwire](https://reader030.fdocuments.us/reader030/viewer/2022032502/55b7ab96bb61eb105b8b4828/html5/thumbnails/7.jpg)
Singleton or not
// singletonval dep1 = new Dependency1val dep2 = new Dependency2val service = new Service(dep1, dep2)
// one instance per callval dep1 = new Dependency1def dep2 = new Dependency2def service = new Service(dep1, dep2)
![Page 8: Compile time dependency injection in Play 2.4 with macwire](https://reader030.fdocuments.us/reader030/viewer/2022032502/55b7ab96bb61eb105b8b4828/html5/thumbnails/8.jpg)
val or lazy val
val dep1 = new Dependency1val service = new Service(dep1, dep2)val dep2 = new Dependency2
java.lang.NullPointerException
lazy val dep1 = new Dependency1lazy val service = new Service(dep1, dep2)lazy val dep2 = new Dependency2
![Page 9: Compile time dependency injection in Play 2.4 with macwire](https://reader030.fdocuments.us/reader030/viewer/2022032502/55b7ab96bb61eb105b8b4828/html5/thumbnails/9.jpg)
Complex dependency tree
lazy val dep1 = new Dependency1lazy val dep2 = new Dependency2lazy val dep3 = new Dependency3lazy val dep4 = new Dependency4lazy val dep5 = new Dependency5(dep3, dep4)lazy val dep6 = new Dependency6(dep2, dep4)lazy val dep7 = new Dependency7(dep5, dep6)lazy val service = new Service(dep1, dep2, dep3, dep4, dep7)
![Page 10: Compile time dependency injection in Play 2.4 with macwire](https://reader030.fdocuments.us/reader030/viewer/2022032502/55b7ab96bb61eb105b8b4828/html5/thumbnails/10.jpg)
With macwire
import com.softwaremill.macwire._
lazy val dep1 = wire[Dependency1]lazy val dep2 = wire[Dependency2]lazy val dep3 = wire[Dependency3]lazy val dep4 = wire[Dependency4]lazy val dep5 = wire[Dependency5]lazy val dep6 = wire[Dependency6]lazy val dep7 = wire[Dependency7]lazy val service = wire[Service]
And that's all!
![Page 11: Compile time dependency injection in Play 2.4 with macwire](https://reader030.fdocuments.us/reader030/viewer/2022032502/55b7ab96bb61eb105b8b4828/html5/thumbnails/11.jpg)
macwire
● Macwire resolves dependencies based on thetype– Compile error when ambiguous– No good idea to have a String as dependency ;)
![Page 12: Compile time dependency injection in Play 2.4 with macwire](https://reader030.fdocuments.us/reader030/viewer/2022032502/55b7ab96bb61eb105b8b4828/html5/thumbnails/12.jpg)
Macwire
● https://github.com/adamw/macwire● Other features
– Accessing wired dynamically– Interceptors– Qualifiers
![Page 13: Compile time dependency injection in Play 2.4 with macwire](https://reader030.fdocuments.us/reader030/viewer/2022032502/55b7ab96bb61eb105b8b4828/html5/thumbnails/13.jpg)
Integration of macwire with Play
● Play 2.3– Everything checked at compile time, expect...
routing... :(● Play 2.4
– Everything checked at compile time!– https://github.com/yanns/TPA/pull/1/files
● Demo
![Page 14: Compile time dependency injection in Play 2.4 with macwire](https://reader030.fdocuments.us/reader030/viewer/2022032502/55b7ab96bb61eb105b8b4828/html5/thumbnails/14.jpg)
Macwire interceptor
● Ex: monitor performance of ws calls:– MonitoringInterceptor
lazy val videoGateway: VideoGateway = logDuration(wire[VideoGateway])
lazy val logDuration = MonitoringInterceptor.logDuration
[debug] duration - 15ms for gateways.VideoGateway#top()[debug] duration - 5ms for gateways.PlayerGateway#findPlayer(2)[debug] duration - 7ms for gateways.PlayerGateway#findPlayer(1)[debug] duration - 4ms for gateways.PlayerGateway#findPlayer(3)[debug] duration - 23ms for services.TopVideoService#topVideos()
![Page 15: Compile time dependency injection in Play 2.4 with macwire](https://reader030.fdocuments.us/reader030/viewer/2022032502/55b7ab96bb61eb105b8b4828/html5/thumbnails/15.jpg)
Integration of macwire with Play 2.4
● build.sbt:libraryDependencies ++= Seq( "com.softwaremill.macwire" %% "macros" %"1.0.1", "com.softwaremill.macwire" %% "runtime" %"1.0.1")
routesGenerator :=play.routes.compiler.InjectedRoutesGenerator
● Customer loader inapplication.conf:
play.application.loader=globals.TBAApplicationLoader
● Custom loader:package globals
import controllers.Assetsimport play.api.ApplicationLoader.Contextimport play.api._import play.api.libs.ws.ning.NingWSComponentsimport play.api.routing.Routerimport router.Routes
class TBAApplicationLoader extends ApplicationLoader { override def load(context: Context): Application = { Logger.configure(context.environment) (new BuiltInComponentsFromContext(context) withTBAComponents).application }}
trait TBAComponents extends BuiltInComponents // standard play components with NingWSComponents // for wsClient with TBAApplication {
import com.softwaremill.macwire._
lazy val assets: Assets = wire[Assets] lazy val router: Router = wire[Routes] withPrefix "/"}
![Page 16: Compile time dependency injection in Play 2.4 with macwire](https://reader030.fdocuments.us/reader030/viewer/2022032502/55b7ab96bb61eb105b8b4828/html5/thumbnails/16.jpg)
Test application for IT testsval context = ApplicationLoader.createContext( new Environment(new File("."), ApplicationLoader.getClass.getClassLoader, Mode.Test))
class TBAApplicationLoaderMock extends ApplicationLoader { override def load(context: Context): Application = { new BuiltInComponentsFromContext(context) with TBAComponents { override lazy val wsClient: WSClient = MockWS(SimulatedPlayerBackend.routes) }.application }}
implicit val application = new TBAApplicationLoaderMock().load(context)val server = TestServer(9000, application)
running(server) { WsTestClient.withClient { ws ⇒ val response = await(ws.url(s"http://localhost:9000/player/$playerId").get()) response.status shouldEqual OK }}
![Page 17: Compile time dependency injection in Play 2.4 with macwire](https://reader030.fdocuments.us/reader030/viewer/2022032502/55b7ab96bb61eb105b8b4828/html5/thumbnails/17.jpg)
The End
Questions?