+ All Categories
Home > Technology > Compile time dependency injection in Play 2.4 with macwire

Compile time dependency injection in Play 2.4 with macwire

Date post: 28-Jul-2015
Category:
Upload: yanns
View: 1,146 times
Download: 0 times
Share this document with a friend
17
DI in Play 2.4 Compile time Dependency Injection with macwire Yann Simon
Transcript
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?


Recommended