Spring bean wiring lost when deployed as WAR on Heroku

\u0000No idea what is happening, but wanted to share to see if anyone has ever seen this.\n\nVaadin 14 with Spring Boot. Straightforward application with views, backing components and a PostgreSQL database. Locally, the application incl. the bean wiring works well.\n\nWhen deployed as WAR on Heroku, the bean wiring does not. Several beans that should have been injected are not, which causes NPEs.\n\nAt the points where this happens, I am able to overcome with a simple:\n\n\nif (userService == null)\n{\n\tlog.error(\"userService is null. No idea why, but loading directly from Context\");\n\tuserService = (UserService) ApplicationContextProvider.getApplicationContext().getBean(\"userService\");\n}\n...\n\n\nafter which the application works as it should.\n\nThe @ComponentScan setting in the main Application entry point is correct.\n\nHas anyone seen this before…? What would be the best approach to debug…?\n\n\uD83D\uDE4F\uD83C\uDFFB

The fact that it works locally speaks against my suspicions, but just to be sure; are you using field-injection or constructor-injection? And what class is this in? And where inside the class is the shown code?

It was in a normal View where I used setter injection to inject a backing Service. I am calling a method that uses this Service via a lambda expression. (To be fair: I was missing a @Repository annotation on a backing service, but fixing this did not solve the problem. Strangely enough, when run in my local environment the application did not choke on this.)

I’ve just tried with constructor-injection, but now I am getting:

  There was an exception while trying to navigate to '[route]
' with the exception message 'Unable to create an instance of '[ExampleView]
'. Make sure the class has a public no-arg constructor.'

With stacktrace:

  2020-03-06T11:36:18.487063+00:00 app[web.1]
: java.lang.IllegalArgumentException: Unable to create an instance of 'dk.mhm.ExampleView'. Make sure the class has a public no-arg constructor.
  2020-03-06T11:36:18.487065+00:00 app[web.1]
: at com.vaadin.flow.internal.ReflectTools.createProxyInstance(ReflectTools.java:517)
  2020-03-06T11:36:18.487067+00:00 app[web.1]
: at com.vaadin.flow.internal.ReflectTools.createInstance(ReflectTools.java:449)
  2020-03-06T11:36:18.487069+00:00 app[web.1]
: at com.vaadin.flow.di.DefaultInstantiator.create(DefaultInstantiator.java:137)
  2020-03-06T11:36:18.487070+00:00 app[web.1]
: at com.vaadin.flow.di.DefaultInstantiator.getOrCreate(DefaultInstantiator.java:66)
  2020-03-06T11:36:18.487070+00:00 app[web.1]
: at com.vaadin.flow.di.Instantiator.createRouteTarget(Instantiator.java:158)
  2020-03-06T11:36:18.487072+00:00 app[web.1]
: at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.lambda$getRouteTarget$1(AbstractNavigationStateRenderer.java:127)
  2020-03-06T11:36:18.487073+00:00 app[web.1]
: at java.base/java.util.Optional.orElseGet(Optional.java:362)
  ...

Constructor injection is shown in [this example]
(https://github.com/vaadin/flow-spring-examples/blob/master/src/main/java/org/vaadin/spring/tutorial/ViewComponent.java), where there is no public no-arg constructor.

After a suggestion from Christoph Frick on Gitter, I have been able to replay this problem locally, when I deploy my application as a WAR to Tomcat 9. The beans are not wired up correctly. I have no problem when starting the application with mvn spring-boot:run.

Does anyone else ever experience bean wiring problems on Tomcat…?

I have always used Spring which takes care of CDI. I guess this is what happened when you ran the project locally with spring-boot.
There are several documentation pages about [CDI without spring]
(https://vaadin.com/docs/v14/flow/cdi/tutorial-cdi-basic.html). You’ll need the [vaadin-cdi]
(https://vaadin.com/directory/component/vaadin-cdi/) add-on I think I read somewhere in there that you indeed need a no-arg constructor, which is not the case when using spring.

Thank you, Kaspar. It is starting to make sense.

My main Application extends SpringBootServletInitializer, which means that Spring Boot is initialized and all the beans are wired up when the application is started with mvn spring-boot:run. It seems that this initialization does not happen automatically when the application is deployed as WAR to Tomcat.

I dó use Spring, I just need to find a way to initialize Spring on startup of the servlet container.