Compile time dependency injection in Play 2.4 with macwire

17
DI in Play 2.4 Compile time Dependency Injection with macwire Yann Simon

Transcript of Compile time dependency injection in Play 2.4 with macwire

Page 1: Compile time dependency injection in Play 2.4 with macwire

DI in Play 2.4Compile time

Dependency Injection

with macwire

Yann Simon

Page 2: Compile time dependency injection in Play 2.4 with macwire

Dependency Injection

RuntimeVS

Compile Time

Page 3: Compile time dependency injection in Play 2.4 with macwire

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

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

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

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

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

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

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

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

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

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

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

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

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

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

The End

Questions?