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:
- Go to
https://start.spring.io/
. - Select
Maven Project
withJava
and Spring Boot version 1.5.10 and type inVaadin
in the “search for dependencies” bar. - Enter the group name as
jcg.zheng.demo
and artifact asvaadin-spring-demo
. - Click the
Generate Project
button.
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 @ComponentScan
with 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
.
Click the “UI_Scoped View” button. It will display a response from DemoUIScopedBean
.
Click the “View_Scoped View” button. It will display a message from DemoViewScopeBean
.
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.
You can download the full source code of this example here: Vaadin Spring Example
Hi, I’m started with vaadin recently, you could make a proyect where you to explain read excel? Please
Excellent.
You’re the BEST