Thoughts on migrating Play 2.3 to 2.5 with a Spring dependency

545 views
Skip to first unread message

Jacco

unread,
Nov 8, 2016, 2:20:35 PM11/8/16
to Play Framework

Hi,

 

Currently I'm working on upgrading a Play 2.3 application to the latest 2.5 version. I use play in combination with spring for dependency injection and for example Spring Data and AMQP. I'm currently checking which kind of solution would fit best for the application since Guice has been introduced in Play 2.4 and it isn't that trivial anymore to combine Play with Spring.

 

Searching for a solution I found several nice approaches and focused on two of them.

 

- The first solution is build by Remi Thieblin and replaces Guice by Spring (https://github.com/remithieblin/play-spring-loader)

- The second solution is build by Eva Maciejko using Spring in combination with Guice (https://github.com/daxyonis/play25-java-spring)

 

First of all I'm glad that both Remi Thieblin and Eva Maciejko shared their solution and I was wondering what your experiences are with the choosen approaches and what your experiences are with either replacing Guice completely or combining it with Spring within a Java project.

 

My reflections on both solution untill now:

  1. First approach (replace completely)
    1. Makes it more transparent because of using Spring DI completely instead of a combination with Guice
    2. I ran into troubles with auto-wiring play.i18n.MessagesApi within play.data.FormFactory version 2.5.9 (Java) and had different results with older version 2.5.4
  2. Second approach (combine Spring and Guice)
    1. A very compact solution by just instantiating a Spring context within the play module and registering the play specific beans within the spring context.
    2. Controllers are also spring beans which leads to a restriction that the routes should be calling static methods which in turn will call the spring bean (looking up bean in Spring context). Else a bean (controller) will be created by Guice which will not find the Spring beans. Ofcourse I could also let Guice create controller beans and let them call the spring controller bean (again by looking the spring bean up in the context). It leads to an extra sort of layer which I would like to avoid.


With the second approach and a stripped down version of my application I was able to start the Spring beans and use them via a static call in the Play controller (by looking them up in the spring context) using Spring for transaction management. Because I still need to do the complete migration I would like to here your thoughts on the best way to approach the migration and the experiences other user have with migrating a project with Spring dependencies to play 2.5.

 

Ofcourse it might also be possible to consider to completely migrate to Guice and try to integrate components like Spring data by alternatives for Guice.

 

Kind regards,

Jacco

Greg Methvin

unread,
Nov 17, 2016, 11:02:39 PM11/17/16
to play-framework
Hi Jacco,

The first solution (replacing the entire DI system) is the ideal.

I think you have a problem with play.i18n.MessageApi because there's no explicit binding for it in Play. There should be one. It probably got removed unintentionally, and likely no one ever noticed yet because Guice has the ability to create it without an explicit binding. I merged a PR with the fix, which was included in the 2.5.10 release: https://github.com/playframework/playframework/pull/6711.

If you have other issues, please let us know and provide details of the errors you're getting so we can investigate the problem. It seems like for Java the third-party implementations have lagged behind a bit, so there's likely a bit more work we need to do to make sure they work properly, but we still want to make Play as agnostic as possible when it comes to dependency injection implementation.

Thanks,
Greg

--
You received this message because you are subscribed to the Google Groups "Play Framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to play-framework+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/7f1bfebf-71d7-4607-8b2a-15c3497e829d%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Greg Methvin
Senior Software Engineer

Jacco

unread,
Dec 5, 2016, 3:54:45 AM12/5/16
to Play Framework
Hi Greg,

Thank you very much for your reply and the fix. My apologies for my late reply. I have migrated the project to Play 2.5.9 a while ago by using a combination of spring DI and Guice (for the moment) and will look into replacing the DI completely on a later date. Replacing the entire DI is also my preferred way. My first concern was now making sure that I was back on track with the latest version of Play and the new way of working by using the Play components via DI.

Kind regards,
Jacco

Op vrijdag 18 november 2016 05:02:39 UTC+1 schreef Greg Methvin:
To unsubscribe from this group and stop receiving emails from it, send an email to play-framewor...@googlegroups.com.

Jacco

unread,
Dec 7, 2016, 1:34:21 PM12/7/16
to Play Framework

Hi Greg,

 

I updated the project to use the latest play version 2.5.10 with Spring for DI and ran into the issue that there is a class play.api.inject.DefaultApplicationLifecycle which has a default constructor combined with the @Inject annotation. This leads to a problem creating a singleton bean within spring as it requires at least one argument when autowiring is used for constructor arguments.

 

play.api.UnexpectedException: Unexpected exception[IllegalStateException: Autowired annotation requires at least one argument: public play.api.inject.DefaultApplicationLifecycle()]

 

Is it neccessary for Play (Guice DI) to have the @Inject added to a default constructor or could this be changed in a later version of play so that Spring is also able to instantiate the DefaultApplicationLifecycle class?

 

Kind regards,

Jacco


Op maandag 5 december 2016 09:54:45 UTC+1 schreef Jacco:

Greg Methvin

unread,
Dec 7, 2016, 9:52:40 PM12/7/16
to play-framework
Hi Jacco,

It's not necessary if you use Guice to include @Inject on default constructors, but there is an option to require it in Guice, and I think other DI frameworks may require it, so we've chosen to annotate empty constructors. I believe this is also good practice as it makes it clear that a class is intended to be used with dependency injection.

I would hope it is somehow possible to disable or work around in Spring. Assuming you're using https://github.com/remithieblin/play-spring-loader you may want to report a bug there.

The other option, if we had to, is to change the bindings so it uses a provider or some other standard JSR-330 way to create those objects (Play uses a standard binding language that is then translated to Guice, Spring, et al. by the application loader).

To unsubscribe from this group and stop receiving emails from it, send an email to play-framework+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/d556a3ac-79be-45ba-b6b4-2e7b8a1adfed%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Jacco

unread,
Dec 9, 2016, 3:27:35 AM12/9/16
to Play Framework
Hi Greg,

It seems like Spring DI is not implementing javax.inject.Inject (@Autowired) correctly (http://docs.oracle.com/javaee/7/api/javax/inject/Inject.html) because in the API it is stated that "Injectable constructors are annotated with @Inject and accept zero or more dependencies as arguments" which is not what Spring does. I don't think that reporting a bug for the play-spring-loader is going to help as Spring itself is the problem here. I spent some time checking if it is possible to toggle an option within Spring to make it accept an autowired constructor without arguments but (for now) I didn't find such an option. I think I hit a real problem here with integrating Spring that is stopping me from completely replacing the DI with Spring unfortunately.

Kind regards,
Jacco


Op donderdag 8 december 2016 03:52:40 UTC+1 schreef Greg Methvin:

Jacco

unread,
Dec 10, 2016, 2:55:05 PM12/10/16
to Play Framework
Hi Remi and Greg,
 
The specific problem with Spring in combination with Play 2.5.10 can be reproduced by using https://github.com/remithieblin/play24_spring as a starting point (Spring DI). This project runs fine with play 2.5.4 but when using 2.5.10 the problem regarding autowiring the play.api.inject.DefaultApplicationLifecycle occurs. I included the stacktrace:

play.api.UnexpectedException: Unexpected exception[IllegalStateException: Autowired annotation requires at least one argument: public play.api.inject.DefaultApplicationLifecycle()]
at play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1$$anonfun$1.apply(DevServerStart.scala:180)
at play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1$$anonfun$1.apply(DevServerStart.scala:131)
at scala.Option.map(Option.scala:146)
at play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1.apply(DevServerStart.scala:131)
at play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1.apply(DevServerStart.scala:129)
at scala.util.Success.flatMap(Try.scala:231)
at play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1.apply(DevServerStart.scala:129)
at play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1.apply(DevServerStart.scala:121)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.liftedTree1$1(Future.scala:24)
at scala.concurrent.impl.Future$PromiseCompletingRunnable.run(Future.scala:24)
at java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1402)
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
Caused by: java.lang.IllegalStateException: Autowired annotation requires at least one argument: public play.api.inject.DefaultApplicationLifecycle()
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:281)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors(AbstractAutowireCapableBeanFactory.java:1069)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1042)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:305)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:301)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:196)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1145)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1069)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:967)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:813)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:741)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:185)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1143)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1046)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:305)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:301)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:196)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:834)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:537)
at com.actimust.play.spring.SpringBuilder.springInjector(SpringBuilder.scala:181)
at com.actimust.play.spring.SpringApplicationBuilder.injector(SpringApplicationBuilder.scala:139)
at com.actimust.play.spring.SpringApplicationBuilder.build(SpringApplicationBuilder.scala:135)
at com.actimust.play.spring.SpringApplicationLoader.load(SpringApplicationLoader.scala:20)
at play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1$$anonfun$1$$anonfun$2.apply(DevServerStart.scala:168)
at play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1$$anonfun$1$$anonfun$2.apply(DevServerStart.scala:164)
at play.utils.Threads$.withContextClassLoader(Threads.scala:21)
at play.core.server.DevServerStart$$anonfun$mainDev$1$$anon$1$$anonfun$get$1$$anonfun$apply$1$$anonfun$1.apply(DevServerStart.scala:164)
... 14 common frames omitted
 
Kind regards,
Jacco

Op vrijdag 9 december 2016 09:27:35 UTC+1 schreef Jacco:

Greg Methvin

unread,
Dec 12, 2016, 5:11:20 AM12/12/16
to play-framework
There may well be a way to work around this in play-spring-loader, but I went ahead and submitted an issue on the Spring JIRA to see if we can get it fixed in Spring itself: https://jira.spring.io/browse/SPR-15005. Feel free to update with more info.

I got the impression that many Spring users actually advocate the use of @Inject as opposed to @Autowired because it's more standard, so you'd think they'd have an interest in supporting zero-argument constructors.

To unsubscribe from this group and stop receiving emails from it, send an email to play-framework+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/29a3724a-1298-45f3-93c5-2c1850520ea4%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
Greg Methvin
Tech Lead - Play Framework

Jacco

unread,
Dec 12, 2016, 2:21:02 PM12/12/16
to Play Framework
Hi Greg,

Thank you very much for your support and for submitting an issue on the Spring JIRA. I completely agree with you on the usage of @Inject instead of @Autowired. With my current solution (combining Spring and Guice together) I can continue for now in staying up-to-date with the latest Play version and will try replacing the DI completely when the issue within Spring has been solved.

Kind regards,
Jacco

Op maandag 12 december 2016 11:11:20 UTC+1 schreef Greg Methvin:

Greg Methvin

unread,
Dec 15, 2016, 3:56:59 PM12/15/16
to play-framework
The change has been made to Spring now: https://github.com/spring-projects/spring-framework/commit/8b5ee4ef9150dd3294e005225c215a1fa6f52fa1. I think it should be in 4.3.5-SNAPSHOT.

To unsubscribe from this group and stop receiving emails from it, send an email to play-framework+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/561ff33e-b3e0-425b-b585-d7ba5bd0a1c8%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Jacco

unread,
Dec 18, 2016, 1:14:11 PM12/18/16
to Play Framework
Hi Greg,

That is great news. Thanks again!

Kind regards,
Jacco

Op donderdag 15 december 2016 21:56:59 UTC+1 schreef Greg Methvin:

sambit...@xvela.com

unread,
Mar 2, 2017, 4:55:09 PM3/2/17
to Play Framework
Thanks Greg for the wonderful work. I am in the midst of an upgrade from Play 2.3 to (Play 2.5.12 + Spring) and using your PlaySpringLoader (https://github.com/remithieblin/play-spring-loader) to get by. I ran into Crypto circular dependency issue even if I followed the suggestion:
You can exclude bindings. This exclude is needed (see below for explanation): play.bindings.disabled += "play.api.libs.Crypto"
 
Turns out, there are two Crypto classes and I have to exclude both to resolve the circular dependency issue. Here is how it looks in my env:
play.bindings.disabled += "play.api.libs.Crypto"
play.bindings.disabled += "play.libs.Crypto"

Thanks
- Sambit

Greg Methvin

unread,
Mar 2, 2017, 6:53:37 PM3/2/17
to play-framework
Thanks. Most of the credit goes to Remi Thieblin, author of play-spring-loader. You should report a bug there if you find anything else incorrect.

Cheers,
Greg

To unsubscribe from this group and stop receiving emails from it, send an email to play-framework+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/aab76b74-545c-4cc6-a526-befbf61887d7%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Sambit Mishra

unread,
Mar 2, 2017, 9:43:00 PM3/2/17
to Play Framework
Thanks Remi. Should have mentioned this earlier, but never the less, appreciate both of you and the community for help.

Aleksandr Nedorezov

unread,
Oct 7, 2017, 10:06:53 PM10/7/17
to Play Framework
Hi,

Any thoughts on how to get Spring to work with Play Framework version 2.6?

I am currently updating play 2.2 application to play 2.6, and already followed 2.2->2.3, 2.3->2.4, 2.4->2.5, 2.5->2.6 migration guides, however it seems that I can't configure Spring properly in play 2.6 application, since my autowired services are always null.

See https://stackoverflow.com/questions/46347521/autowired-spring-service-is-null-in-play-framework-2-6-application for details.

I have tried the solutions suggested in this post. However, 

the first one gives MethodNotFound error

NoSuchMethodError: play.api.Environment$.simple$default$2()Lscala/Enumeration$Value;

No source available, here is the exception stack trace:

->java.lang.NoSuchMethodError: play.api.Environment$.simple$default$2()Lscala/Enumeration$Value;
     com.actimust.play.spring.SpringApplicationBuilder.<init>(SpringApplicationBuilder.scala:26)
     com.actimust.play.spring.SpringApplicationLoader.<init>(SpringApplicationLoader.scala:16)

and the second doesn't seem to change anything in applications behaviour.

I am interested on your thoughts on what would be the best approach to migrate play 2.2 project with Spring dependencies to play 2.6.

Greg Methvin

unread,
Oct 7, 2017, 11:48:38 PM10/7/17
to play-framework
The library is not compatible with 2.6. It just needs to be recompiled against Play 2.6. I submitted a PR: https://github.com/remithieblin/play-spring-loader/pull/6

If you checkout that branch and use "sbt publishLocal" you can try the 1.0.0-SNAPSHOT version from your local repository and see if that works for you.

--
You received this message because you are subscribed to the Google Groups "Play Framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to play-framework+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/bcbd210e-318f-41dc-b91f-ade44ef4adac%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Aleksandr Nedorezov

unread,
Oct 9, 2017, 9:12:40 AM10/9/17
to Play Framework
Thanks a lot!

Can you please provide some tips on how to write a module mentioned in play-spring-loader docs? I am talking about MyModule referenced from application.conf.

play.modules.enabled += "com.demo.spring.MyModule"

play.spring.configs = ["com.example.PlaySpringDIConfiguration"]

To unsubscribe from this group and stop receiving emails from it, send an email to play-framewor...@googlegroups.com.

Greg Methvin

unread,
Oct 9, 2017, 7:16:00 PM10/9/17
to play-framework
I think that's meant to show that you can use a regular Play module and it will create Spring bindings. That would mainly be interesting if you're writing a third party module and want it to be compatible with multiple DI frameworks. In Play the documentation for that is here: https://www.playframework.com/documentation/2.6.x/ScalaPlayModules#Creating-and-migrating-Play-modules

To unsubscribe from this group and stop receiving emails from it, send an email to play-framework+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/d901209c-58a8-4c8f-a34e-e13e89eb5d37%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Greg Methvin

unread,
Oct 15, 2017, 12:02:56 PM10/15/17
to play-framework
FYI, I released a version of Remi's loader at https://github.com/playframework/play-spring-loader. We (the Play team) don't have any plans to add to it at the moment, but we'd appreciate community contributions with tests, documentation, and additional features. We are not Spring experts so help from Spring users is welcome!
Reply all
Reply to author
Forward
0 new messages