Vaadin

Vaadin Spring Example

1. Introduction

Vaadin is a web application framework written in Java, and is built on Google Web Toolkit from Vaadin Ltd.

Spring Framework is a Java application framework that provides many useful services for building applications.

Vaadin provides a Vaadin Spring add-on based on the core parts of vaadin4spring to make classes for UI and View as Spring managed beans so Spring dependency injection can be used.

In this example, I will demonstrate how to build a Single Paged Application (SPA) with Vaadin Spring.

2. Technologies Used

The example code in this article was built and run using:

  • Java 1.8.101 (1.8.x will do fine)
  • Maven 3.3.9 (3.3.x will do fine)
  • Eclipse Mars (Any Java IDE would work)
  • Vaadin 8.3.0 (7.x will do fine)
  • Vaadin Spring Boot 1.5.10.RELEASE

3. Spring-boot Vaadin Web Application

The easiest way to generate a Spring-boot Vaadin application is via the Spring starter tool with the steps below:

  1. Go to https://start.spring.io/.
  2. Select Maven Project with Java and Spring Boot version 1.5.10 and type in Vaadin in the “search for dependencies”  bar.
  3. Enter the group name as jcg.zheng.demo and artifact as vaadin-spring-demo.
  4. Click the Generate Project button.

Generate Vaadin spring-boot Project
Figure 1 Generate Vaadin spring-boot Project

A maven project will be generated and downloaded to your workstation. Import it into your Eclipse workspace.

3.1 POM

The generated pom.xml includes vaadin-spring-boot-starter. There is no modification needed in this example.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>jcg.zheng.demo</groupId>
	<artifactId>vaadin-spring-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>vaadin-spring-demo</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.10.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<vaadin.version>8.3.0</vaadin.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>com.vaadin</groupId>
			<artifactId>vaadin-spring-boot-starter</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>com.vaadin</groupId>
				<artifactId>vaadin-bom</artifactId>
				<version>${vaadin.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>


</project>

3.2 Vaadin Spring Demo Application

The generated VaadinSpringDemoApplication.java is annotated with @SpringBootApplication. It is equivalent to using  @Configuration@EnableAutoConfiguration, and @ComponentScanwith their default attributes. In this example there is also no modification needed.

VaadinSpringDemoApplication.java

package jcg.zheng.demo.spring;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class VaadinSpringDemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(VaadinSpringDemoApplication.class, args);
	}
}

Execute mvn clean install to build the project. You should be able to start it as a Java Application.

4. Single Paged Vaadin Application

If you have not worked with Vaadin before, please check it out here.

Vaadin-Spring add-on API includes the annotations below:

  • @SpringComponent
  • @SpringUI
  • @SpringView
  • @SpringViewDisplay
  • @UIScope
  • @VaadinSessionScope
  • @ViewScope

The easiest way to set up your navigation in a SPA is to use the @SpringViewDisplay on the UI class, in which case the whole contents of the UI is replaced based on the navigation. In this example, we will create a main UI class, which contains two navigation buttons. It will switch to the dedicated view when the correct button is clicked.

4.1 Spring Beans

Vaadin Spring @SpringComponent annotation allows for automatic detection of beans managed by Spring.

In case you were wondering, @SpringComponent is exactly the same as the regular Spring @Component, but has been given an alias, because Vaadin already has a Component interface.

In this example, we will create two Spring Beans, annotate them with @ViewScope and @UIScope respectively.

4.1.1 Bean with @ViewScope

The lifecycle of View-scoped beans starts when the user navigates to a view referring to the object and ends when the user navigates out of the view or when the UI is closed or expires.

In this step, we will create DemoViewScopeBean with @ViewScope annotation. This bean will be used later at step 4.2.1.

DemoViewScopeBean.java

package jcg.zheng.demo.spring;

import com.vaadin.spring.annotation.SpringComponent;
import com.vaadin.spring.annotation.ViewScope;

@SpringComponent
@ViewScope
public class DemoViewScopeBean {
	public String getData() {
		return "Same bean instance for same view. bean="
				+ toString();

	}
}

4.1.2 Bean with @UIScope

The lifecycle of UI-scoped beans is bound between the initialization and closing of a UI. UI-scoped beans are uniquely identified within a UI instance, that is, a browser window or tab. When injecting a bean, the same instance bean will be used if within the same UI.

In this step, we will create DemoUIScopeBean with @UIScope annotation. This bean will be used later at steps 4.2.1 and 4.2.2.

DemoUIScopeBean.java

package jcg.zheng.demo.spring;

import com.vaadin.spring.annotation.SpringComponent;
import com.vaadin.spring.annotation.UIScope;

@SpringComponent
@UIScope
public class DemoUIScopeBean {
	public String getData() {
		return "Same bean instance for same UI. bean=" + toString();
	}
}

4.2 Spring Views

The @SpringView annotation enables Spring injection features in the view classes. The SpringViewProvider fetches the views from the Spring application context and registers them so that these views can be managed by @SpringViewDisplay.

In this step, we will create ViewScopeView and DefaultView and annotate them with @ViewScope and UIScopeView and ErrorView and annotate them with @UIScope.

4.2.1 View with @ViewScope

In this step, we will create two views – DefaultView and ViewScopeView with @ViewScope annotation.

DefaultView is the one used when the web application starts.

DefaultView.java

package jcg.zheng.demo.spring.view;

import javax.annotation.PostConstruct;

import com.vaadin.navigator.View;
import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent;
import com.vaadin.spring.annotation.SpringView;
import com.vaadin.ui.Label;
import com.vaadin.ui.VerticalLayout;

@SpringView(name = DefaultView.VIEW_NAME)
public class DefaultView extends VerticalLayout implements View {

	private static final long serialVersionUID = -3903205444585313680L;
	public static final String VIEW_NAME = ""; //default

    @PostConstruct
    void init() {
        addComponent(new Label("Welcome to Vaadin-Spring Demo!!"));
    }

    @Override
    public void enter(ViewChangeEvent event) {
        // This view is constructed in the init() method()
    }
}

ViewScopeView is the one used when the View_Scoped View button is clicked.

ViewScopeView.java

package jcg.zheng.demo.spring.view;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;

import com.vaadin.navigator.View;
import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent;
import com.vaadin.spring.annotation.SpringView;
import com.vaadin.ui.Label;
import com.vaadin.ui.VerticalLayout;

import jcg.zheng.demo.spring.DemoUIScopeBean;
import jcg.zheng.demo.spring.DemoViewScopeBean;

@SpringView(name = ViewScopeView.VIEW_NAME)
public class ViewScopeView extends VerticalLayout implements View {

	private static final long serialVersionUID = 5784972560238064106L;

	public static final String VIEW_NAME = "view";
	
	// A new instance will be created for every view instance created
    @Autowired
    private DemoViewScopeBean viewBean;
    
    // The same instance will be used by all views of the UI
    @Autowired
    private DemoUIScopeBean uiBean;
    

	@PostConstruct
	void init() {
		addComponent(new Label("This is a view scoped view"));
		addComponent(new Label( uiBean.getData()));
		addComponent(new Label( viewBean.getData()));
	}

	@Override
	public void enter(ViewChangeEvent event) {
		// This view is constructed in the init() method()
	}
}

4.2.2 View with @UIScope

Please note that @UIScope needs to be before @SpringView because @SpringView has default attribute of @ViewScope.

In this step, we will create two views – UIScopeView and ErrorView with @UIScope annotation.
UIScopeview which contains a vertical layout and displays a label from DemoUIScopeBean.

UIScopeView.java

package jcg.zheng.demo.spring.view;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;

import com.vaadin.navigator.View;
import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent;
import com.vaadin.spring.annotation.SpringView;
import com.vaadin.spring.annotation.UIScope;
import com.vaadin.ui.Label;
import com.vaadin.ui.VerticalLayout;

import jcg.zheng.demo.spring.DemoUIScopeBean;

//Annotation order is matter here, @UIScope is before @SpringView
@UIScope
@SpringView(name = UIScopeView.VIEW_NAME)
public class UIScopeView extends VerticalLayout implements View {

	private static final long serialVersionUID = -3089511061636116441L;

	public static final String VIEW_NAME = "ui";

	@Autowired
	private DemoUIScopeBean uiBean;

	@PostConstruct
	void init() {
		addComponent(new Label("This is a UI scoped view."));
		addComponent(new Label("uiBean says: " + uiBean.getData()));
	}

	@Override
	public void enter(ViewChangeEvent event) {
		// This view is constructed in the init() method()
	}
}

ErrorView must be annotated with @UIScope because the SPA handles errors for the entire UI.

ErrorView.java

package jcg.zheng.demo.spring.view;

import javax.annotation.PostConstruct;

import com.vaadin.navigator.View;
import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent;
import com.vaadin.spring.annotation.SpringView;
import com.vaadin.spring.annotation.UIScope;
import com.vaadin.ui.Label;
import com.vaadin.ui.VerticalLayout;

@UIScope
@SpringView(name = ErrorView.VIEW_NAME)
public class ErrorView extends VerticalLayout implements View {

	private static final long serialVersionUID = -134715779625065266L;
	public static final String VIEW_NAME = "error";

    @PostConstruct
    void init() {
        addComponent(new Label("This is the error view - Oops!"));
    }

    @Override
    public void enter(ViewChangeEvent event) {
        // This view is constructed in the init() method()
    }
}

4.3 Spring UI

Vaadin Spring provides @SpringUI to instantiate UIs and to define the URL mapping for them. The annotated UI will automatically be placed in the UIScope.

At this step, we will create MainUI, which contains two navigation buttons. It will switch to the correct view when the corresponding button is clicked.

MainUI.java

package jcg.zheng.demo.spring.ui;

import com.vaadin.navigator.View;
import com.vaadin.navigator.ViewDisplay;
import com.vaadin.server.VaadinRequest;
import com.vaadin.spring.annotation.SpringUI;
import com.vaadin.spring.annotation.SpringViewDisplay;
import com.vaadin.ui.Button;
import com.vaadin.ui.Component;
import com.vaadin.ui.CssLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Panel;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.themes.ValoTheme;

import jcg.zheng.demo.spring.view.ErrorView;
import jcg.zheng.demo.spring.view.UIScopeView;
import jcg.zheng.demo.spring.view.ViewScopeView;

@SpringUI(path = MainUI.APP_ROOT)
@SpringViewDisplay
public class MainUI extends UI implements ViewDisplay {

	static final String APP_ROOT = "/vaadin-spring-demo";
	private static final String VIEW_SCOPED_VIEW = "View_Scoped View";
	private static final String UI_SCOPED_VIEW = "UI_Scoped View";
	private static final long serialVersionUID = 4967383498113318791L;
	private Panel springViewDisplay;

	@Override
	protected void init(VaadinRequest vaadinRequest) {

		final VerticalLayout root = new VerticalLayout();
		root.setSizeFull();
		setContent(root);

		final CssLayout navigationBar = new CssLayout();
		
		navigationBar.addStyleName(ValoTheme.LAYOUT_COMPONENT_GROUP);
	
		navigationBar.addComponent(createNavigationButton(UI_SCOPED_VIEW, UIScopeView.VIEW_NAME));
		navigationBar.addComponent(new Label(" "));
		navigationBar.addComponent(createNavigationButton(VIEW_SCOPED_VIEW, ViewScopeView.VIEW_NAME));
	
		root.addComponent(navigationBar);

		springViewDisplay = new Panel();
		springViewDisplay.setSizeFull();
		root.addComponent(springViewDisplay);
		root.setExpandRatio(springViewDisplay, 1.0f);

		getNavigator().setErrorView(ErrorView.class);
	}

	private Button createNavigationButton(String caption, final String viewName) {
		Button button = new Button(caption);
		button.addStyleName(ValoTheme.BUTTON_SMALL);
	
		button.addClickListener(event -> getUI().getNavigator().navigateTo(viewName));
		return button;
	}

	@Override
	public void showView(View view) {
		springViewDisplay.setContent((Component) view);
	}

}

5. Demo Time

Execute Run As Java Application.

Spring-boot output indicates the web application is up.

application output

2018-02-25 21:28:50.970  INFO 12152 --- [           main] j.z.d.s.VaadinSpringDemoApplication      : Starting VaadinSpringDemoApplication on SL2LS431841 with PID 12152 (C:\MZheng_Java_workspace\vaadin-spring-demo\target\classes started by shu.shan in C:\MZheng_Java_workspace\vaadin-spring-demo)
2018-02-25 21:28:50.975  INFO 12152 --- [           main] j.z.d.s.VaadinSpringDemoApplication      : No active profile set, falling back to default profiles: default
2018-02-25 21:28:51.110  INFO 12152 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@32464a14: startup date [Sun Feb 25 21:28:51 CST 2018]; root of context hierarchy
2018-02-25 21:28:52.400  WARN 12152 --- [           main] o.s.c.a.ConfigurationClassPostProcessor  : Cannot enhance @Configuration bean definition 'com.vaadin.spring.VaadinConfiguration' since its singleton instance has been created too early. The typical cause is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor return type: Consider declaring such methods as 'static'.
2018-02-25 21:28:53.959  INFO 12152 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http)
2018-02-25 21:28:53.985  INFO 12152 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2018-02-25 21:28:53.990  INFO 12152 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.27
2018-02-25 21:28:54.231  INFO 12152 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2018-02-25 21:28:54.231  INFO 12152 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 3127 ms
2018-02-25 21:28:54.471  INFO 12152 --- [ost-startStop-1] c.v.s.b.i.VaadinServletConfiguration     : Registering Vaadin servlet
2018-02-25 21:28:54.472  INFO 12152 --- [ost-startStop-1] c.v.s.b.i.VaadinServletConfiguration     : Servlet will be mapped to URLs [/vaadinServlet/*, /VAADIN/*]
2018-02-25 21:28:54.505  INFO 12152 --- [ost-startStop-1] c.v.s.b.i.VaadinServletConfiguration     : Setting servlet init parameters
2018-02-25 21:28:54.506  INFO 12152 --- [ost-startStop-1] c.v.s.b.i.VaadinServletConfiguration     : Set servlet init parameter [productionMode] = [false]
2018-02-25 21:28:54.506  INFO 12152 --- [ost-startStop-1] c.v.s.b.i.VaadinServletConfiguration     : Set servlet init parameter [resourceCacheTime] = [3600]
2018-02-25 21:28:54.506  INFO 12152 --- [ost-startStop-1] c.v.s.b.i.VaadinServletConfiguration     : Set servlet init parameter [heartbeatInterval] = [300]
2018-02-25 21:28:54.506  INFO 12152 --- [ost-startStop-1] c.v.s.b.i.VaadinServletConfiguration     : Set servlet init parameter [closeIdleSessions] = [false]
2018-02-25 21:28:54.642  INFO 12152 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: 'dispatcherServlet' to [/]
2018-02-25 21:28:54.645  INFO 12152 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: 'springVaadinServlet' to [/vaadinServlet/*, /VAADIN/*]
2018-02-25 21:28:54.650  INFO 12152 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
2018-02-25 21:28:54.653  INFO 12152 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2018-02-25 21:28:54.653  INFO 12152 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2018-02-25 21:28:54.653  INFO 12152 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]
2018-02-25 21:28:55.274  INFO 12152 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@32464a14: startup date [Sun Feb 25 21:28:51 CST 2018]; root of context hierarchy
2018-02-25 21:28:55.411  INFO 12152 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2018-02-25 21:28:55.413  INFO 12152 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2018-02-25 21:28:55.480  INFO 12152 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-02-25 21:28:55.481  INFO 12152 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-02-25 21:28:55.621  INFO 12152 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-02-25 21:28:55.681  INFO 12152 --- [           main] c.v.s.b.i.VaadinServletConfiguration     : Checking the application context for Vaadin UI mappings
2018-02-25 21:28:55.690  INFO 12152 --- [           main] c.v.s.b.i.VaadinServletConfiguration     : Registering Vaadin servlet of type [com.vaadin.spring.server.SpringVaadinServlet]
2018-02-25 21:28:55.703  INFO 12152 --- [           main] c.v.s.b.i.VaadinServletConfiguration     : Forwarding @SpringUI URLs from {/vaadin-spring-demo=org.springframework.web.servlet.mvc.ServletForwardingController@22db8f4, /vaadin-spring-demo/=org.springframework.web.servlet.mvc.ServletForwardingController@22db8f4}
2018-02-25 21:28:55.704  INFO 12152 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/vaadin-spring-demo] onto handler of type [class org.springframework.web.servlet.mvc.ServletForwardingController]
2018-02-25 21:28:55.705  INFO 12152 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/vaadin-spring-demo/] onto handler of type [class org.springframework.web.servlet.mvc.ServletForwardingController]
2018-02-25 21:28:56.099  INFO 12152 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2018-02-25 21:28:56.220  INFO 12152 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2018-02-25 21:28:56.234  INFO 12152 --- [           main] j.z.d.s.VaadinSpringDemoApplication      : Started VaadinSpringDemoApplication in 5.866 seconds (JVM running for 6.483)
2018-02-25 21:28:56.324  WARN 12152 --- [nio-8080-exec-3] c.v.s.DefaultDeploymentConfiguration     :

Go to http://localhost:8080/vaadin-spring-demo. It  will display “Welcome to Vaadin-Spring Demo” from the DefaultView.

Figure 2 SPA default view

Click the “UI_Scoped View” button. It will display a response from DemoUIScopedBean.

Figure 3 SPA UI_scoped view

Click the “View_Scoped View” button. It will display a message from DemoViewScopeBean.

Figure 4 SPA View_scoped view

Note: @UIScope bean has same instance when clicking the button back and forth. But the @ViewScope bean has a new instance for each click.

6. Summary

In this example, we built a Vaadin Spring Boot web application via the Vaadin-Spring starter and then modified it with UI components annotated with Vaadin-Spring Add-on annotations. We demonstrated that Vaadin Spring Add-on provides an easier way to allow the UI and View classes to access the Spring dependency injection feature.

You can use the Vaadin-Spring in non Spring Boot web application. I have another article with more details, please check it out here.

7. Download the Source Code

This example consists of a Spring-boot Vaadin web application.

Download
You can download the full source code of this example here: Vaadin Spring Example

Mary Zheng

Mary has graduated from Mechanical Engineering department at ShangHai JiaoTong University. She also holds a Master degree in Computer Science from Webster University. During her studies she has been involved with a large number of projects ranging from programming and software engineering. She works as a senior Software Engineer in the telecommunications sector where she acts as a leader and works with others to design, implement, and monitor the software solution.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Cristhian Alexis Galeano Ruiz
Cristhian Alexis Galeano Ruiz
6 years ago

Hi, I’m started with vaadin recently, you could make a proyect where you to explain read excel? Please

Julio Espinoza
5 years ago

Excellent.
You’re the BEST

Back to top button