As a Java user, I feel frustrated to Play 2.4.0, for its new DI

305 views
Skip to first unread message

sunnykaka

unread,
Jul 2, 2015, 2:25:15 AM7/2/15
to play-fr...@googlegroups.com
My project was written with Play 2.3.8, Java and Spring.

Recently I found Play 2.4 was out, and I decide to upgrade my project to Play 2.4.

After struggling for a day, I can't find a way to do that.

The most important reason is that getControllerInstance method was removed from GlobalSettings.

I know Guice is the default DI tool in Play 2.4.0, but my project heavily dependent on Spring, and I don't wan't to rewrite them to much.

In contrast to Scala user, I think Spring is still important to Java user, like transaction management, timer, etc. 

Can play team offer a easy way to use Spring or other DI framework in the project, just bring back getControllerInstance  is enough.

Implements ApplicationLoader is a horrible task to end user.

I don't mind Guice and Spring both in my project.

Thank you very much!


Christian Schmitt

unread,
Jul 2, 2015, 3:01:53 AM7/2/15
to play-fr...@googlegroups.com
Ehm.. you could replace Guice really easily. There is no dependency on Guice, there is just a Dependency on JSR-330 and I think Spring supports the standard..

Out of the box, Play provides dependency injection support based on JSR 330. The default JSR 330 implementation that comes with Play is Guice, but other JSR 330 implementations can be plugged in.


And then you just need to look at:
The last section of that page. You just need to build a SpringApplicationLoader which extends ApplicationLoader and use it to bootstrap your play application., there is nothing fancy or so... it just works.

Christian Schmitt

unread,
Jul 2, 2015, 3:04:25 AM7/2/15
to play-fr...@googlegroups.com

sunnykaka

unread,
Jul 2, 2015, 4:12:53 AM7/2/15
to play-fr...@googlegroups.com
Thanks Christian.

I tried Jroper's work, it can't compile in current version.

I know I can implements ApplicationLoader.

But it's need to understand how DI works in Play, I'm new to Play, my implementation is error-prone.

It's really a common use case, it would be nice if Play Team work on this.

Even though, I will give a try...

sunnykaka

unread,
Jul 6, 2015, 10:36:04 PM7/6/15
to play-fr...@googlegroups.com
I solved this problem.

My goal is upgrade my project to Play 2.4.2, and keep spring as my service and controller class container(I used a lot of spring features so I can't get rid of it easily).

Here is what I did.

1. Create a injector proxy, if Play retrieve a play.mvc.Controller instance from container, the proxy injector will get it from Spring first. In other case, retrieve from Guice.

2. If service and controller class (In Spring Container) need a Play component, e.g. DBApi, use Play.application().injector.instanceOf(DBApi.class) to get it.

So I cant upgrade to Play 2.4 almost without modifying the code.

Here is injector proxy code.

SkGuiceApplicationLoader.scala

class SkGuiceApplicationLoader extends GuiceApplicationLoader {

 
override protected def overrides(context: Context): Seq[GuiceableModule] = {
   
super.overrides(context) ++ Seq[GuiceableModule](
     
bind[Injector].to[SkInjector]
   
)
 
}
}

SkInjector.scala

class SkInjector @Inject() (injector: com.google.inject.Injector) extends GuiceInjector(injector) {

 
/**
   * Get an instance of the given class from the injector.
   */
  override def instanceOf[T](implicit ct: ClassTag[T]) = {
    instanceOf
(ct.runtimeClass.asInstanceOf[Class[T]])
 
}

 
/**
   * Get an instance of the given class from the injector.
   */
  override def instanceOf[T](clazz: Class[T]) = {
   
var bean: Option[T] = None
   
if(classOf[Controller].isAssignableFrom(clazz)) {
     
//BaseGlobal.ctx is my spring context. You can change it to your context.
      bean
= Option(BaseGlobal.ctx.getBean(clazz))
   
}
   
if(bean.isEmpty) {
      bean
= Option(super.instanceOf(clazz))
   
}
    bean
.fold(null.asInstanceOf[T])(x => x)
 
}

 
/**
   * Get an instance bound to the given binding key.
   */
  override def instanceOf[T](key: BindingKey[T]) = {
   
var bean: Option[T] = None
   
if(classOf[Controller].isAssignableFrom(key.clazz)) {
      bean
= Option(BaseGlobal.ctx.getBean(key.clazz))
   
}
   
if(bean.isEmpty) {
      bean
= Option(super.instanceOf(key))
   
}
    bean
.fold(null.asInstanceOf[T])(x => x)
 
}


}

modify application.conf: 

play.application.loader = "common.play.inject.SkGuiceApplicationLoader"


I hope this is helpful.
Reply all
Reply to author
Forward
0 new messages