Difficult using vaadin-spring

I have a working Vaadin application with Spring, and now I want to add vaadin-spring integration so that I can use Spring DI with my Vaadin UI views.

The current working setup is:

  1. Web application configuration through web.xml
  2. Spring Dispatcher servlet declared and mapped to /services/*
  3. Spring bean definitions loaded through applicationContext.xml
  4. Vaadin servlet declared and mapped to /app/*

Vaadin views currently interact with Spring beans via code, obtaining a Spring ApplicationContext using WebApplicationContextUtil.getApplicationContext().

As soon as I add the vaadin-spring dependency, the application breaks. Spring still starts and initializes properly, but Vaadin does not. I did try making the following changes:

  1. Remove Vaadin servlet declaration from web.xml
  2. Add a WebApplicationInitializer to dynamically create and register a SpringServlet and map it to /app/*

The problem seems to be that a Lookup is not getting properly created. If I trace through VaadinServlet.init(), I can see that it is bailing out at this test (around line 126 in the 24.9.9 version I am using):

if (servletService != null || vaadinServletContext
        .getAttribute(Lookup.class) == null) {
    return;
}

Any idea what I need to do to get this working? I feel like I am very close…

Based on your question I guess you are trying to setup Spring MVC instead of using Spring Boot.

Have you checked our documentation how to register the servlet?

I’m not doing it exactly the way it’s described in the document because a) I already have a Spring Dispatcher servlet defined, and b) I want to reuse the ApplicationContext it already generated. Instead I have a WebApplicationInitializer with this code:

   public void onStartup(ServletContext servletContext)
      throws ServletException
   {
      var ac = WebApplicationContextUtils.getWebApplicationContext(servletContext);
      ServletRegistration.Dynamic reg = servletContext.addServlet(
         "VaadinApplicationServlet",
         new SpringServlet(ac, false)
      );
      reg.setAsyncSupported(true);
      reg.addMapping("/app/*");
      reg.setLoadOnStartup(2);
   }

I guess you are missing some important pieces that are provided by VaadinMVCWebAppInitializer.
For example, the inclusion of VaadinApplicationConfiguration configuration class that provide the SpringApplicationContextInit bean that is responsible for initializing the Lookup system, IIRC

Thanks for the tip Marco. With that I was able to get it working.

Here are the key steps I followed to add Vaadin Spring support to my existing Spring WebMVC application.

  1. Remove Vaadin Servlet definition from web.xml
  2. Add Maven dependencies for vaadin-spring and spring-security-web. (Check vaadin-spring POM to find compatible version of spring-security-web)
  3. Add (or modify in my case) a WebMvcConfigurer configuration bean:

Add scan for Vaadin beans using @ComponentScan:

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "com.vaadin.flow.spring" })
@PropertySource(value = {"classpath:application.properties" })
public class AppConfiguration implements WebMvcConfigurer
{

Add an ApplicationListener bean to install the SpringServlet

   @Bean
   public ApplicationListener<ContextRefreshedEvent> getApplicationStartListener()
   {
      return event -> {

         WebApplicationContext ctx = (WebApplicationContext) event.getApplicationContext();

         // we only care about events for the global namespace
         if (ctx instanceof XmlWebApplicationContext wac && wac.getNamespace() == null)
         {
            App app = ctx.getBean(WebConstants.BEANNAME_APP, App.class);
            ServletContext servletContext = ctx.getServletContext();

            var servlet = new SpringServlet(ctx, false);
            var reg = servletContext.addServlet(
               "VaadinApplicationServlet",
               servlet
            );
            reg.setInitParameter(ApplicationConfig.JSR356_MAPPING_PATH, "/app/VAADIN/push");
            reg.setAsyncSupported(true);
            reg.setLoadOnStartup(1);
            reg.addMapping("/app/*");
         }
      }
   }
}

So now the application launches and runs as it did before. Spring DI for my @Routed views now also appears to be working as expected. Thanks for the tip!

Update: This broke when I upgraded to Vaadin 25.

There is a class com.vaadin.flow.spring.security.RequestUtil that is used as a bean but is not annotated with the @Component annotation, so if you’re not using the documented setup process then Spring doesn’t know about it.

The fix was to add the following method to the WebMvcConfigurer subclass:

 @Bean
 public RequestUtil getRequestUtil()
 {
    return new RequestUtil();
 }

One more update for the record in case someone else needs it.

Don’t use @ComponentScan as it may try to define beans that aren’t relevant to your environment (e.g. Spring Security).

Instead, use this annotation on your configuration class:

@Import({ VaadinScopesConfig.class, VaadinApplicationConfiguration.class, VaadinServletConfiguration.class })