Vaadin Spring Add-on
- Spring Overview
- Quick Start with Vaadin Spring Boot
- Installing Vaadin Spring Add-on
- Preparing Application for Spring
- Injecting a UI with @SpringUI
- Scopes
- View Navigation
- Access Control
- Deploying Spring UIs and Servlets
Vaadin Spring and Vaadin Spring Boot add-ons make it easier to use Spring in Vaadin applications. Vaadin Spring enables Spring dependency injection with custom UI and view providers, and provides three custom scopes: UIScope, ViewScope, and VaadinSessionScope.
API documentation for add-ons is available at:
-
Vaadin Spring API at vaadin.com/api/vaadin-spring
-
Vaadin Spring Boot API at vaadin.com/api/vaadin-spring-boot
To learn more about Vaadin Spring, the Vaadin Spring Tutorial gives a hands-on introduction. The source code of the Spring Tutorial demo is available for browsing or cloning at github.com/Vaadin/spring-tutorial.
Spring Overview
Spring Framework is a Java application framework that provides many useful services for building applications, such as authentication, authorization, data access, messaging, testing, and so forth. In the Spring core, one of the central features is dependency injection, which accomplishes inversion of control for dependency management in managed beans. Other Spring features rely on it extensively. As such, Spring offers capabilities similar to CDI, but with integration with other Spring services. Spring is well-suited for applications where Vaadin provides the UI layer and Spring is used for other aspects of the application logic.
Spring Boot is a Spring application development tool that allows creating Spring applications easily. Vaadin Spring Boot builds on Spring Boot to allow creating Vaadin Spring applications easily. It starts up a servlet, handles the configuration of the application context, registers a UI provider, and so forth.
Regarding general Spring topics, we recommend the following Spring documentation:
Dependency Injection
Dependency injection is a way to pass dependencies (service objects) to dependent objects (clients) by injecting them in member variables or initializer parameters, instead of managing the lifecycle in the clients or passing them explicitly as parameters. In Spring, injection of a service object to a client is configured with the @Autowired annotation.
For a very typical example in a web application, you could have a user data object associated with a user session:
@SpringComponent
@VaadinSessionScope
public class User implements Serializable {
private String name;
public void setName(String name) {this.name = name;}
public String getName() {return name;}
}
The @SpringComponent annotation allows for automatic detection of managed beans by Spring. (The annotation is exactly the same as the regular Spring @Component, but has been given an alias, because Vaadin has a Component interface, which can cause trouble.)
Now, if we have a UI view that depends on user data, we could inject the data in the client as follows:
public class MainView extends CustomComponent implements View {
User user;
Label greeting = new Label();
@Autowired
public MainView(User user) {
this.user = user;
...
}
...
@Override
public void enter(ViewChangeEvent event) {
// Then you can use the injected data
// for some purpose
greeting.setValue("Hello, " + user.getName());
}
}
Here, we injected the user object in the constructor. The user object would be created automatically when the view is created, with all such references referring to the same shared instance in the scope of the Vaadin user session.
Contexts and Scopes
Contexts in Spring are services that manage the lifecycle of objects and handle their injection. Generally speaking, a context is a situation in which an instance is used with a unique identity. Such objects are essentially "singletons" in the context. While conventional singletons are application-wide, objects managed by a Spring container can be "singletons" in a more narrow scope: a user session, a particular UI instance associated with the session, a view within the UI, or even just a single request. Such a context defines the lifecycle of the object: its creation, use, and finally its destruction.
Earlier, we introduced a user class defined as session-scoped:
@SpringComponent
@VaadinSessionScope
public class User {
Now, when you need to refer to the user, you can use Spring injection to inject the session-scoped "singleton" to a member variable or a constructor parameter; the former in the following:
public class MainView extends CustomComponent implements View {
@Autowired
User user;
Label greeting = new Label();
...
@Override
public void enter(ViewChangeEvent event) {
greeting.setValue("Hello, " + user.getName());
}
}
Quick Start with Vaadin Spring Boot
The Vaadin Spring Boot is an add-on that allows for easily creating a project that uses Vaadin Spring. It is meant to be used together with Spring Boot, which enables general Spring functionalities in a web application.
You can use the Spring Initializr at start.spring.io website to generate a project, which you can then download as a package and import in your IDE. Pick Vaadin as a dependency on the site to include all the required Vaadin Spring dependencies and auto-configuration.The generated project is a Spring application stub; you need to add at least a UI class to the generated project.
See the Vaadin Spring Tutorial for detailed instructions for using Spring Boot.
Installing Vaadin Spring Add-on
To install the Vaadin Spring and/or Vaadin Spring Boot add-ons, either define them as an Ivy or Maven dependency or download from the Vaadin Directory add-on page at vaadin.com/directory#addon/vaadin-spring or vaadin.com/directory#addon/vaadin-spring-boot. See "Using Vaadin Add-ons" for general instructions for installing and using Vaadin add-ons.
The Ivy dependency is as follows:
<dependency org="com.vaadin" name="vaadin-spring"
rev="latest.release"/>
The Maven dependency is as follows:
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin-spring</artifactId>
<version>LATEST</version>
</dependency>
For Vaadin Spring Boot, depending on vaadin-spring-boot-starter will include all the required Vaadin dependencies.
Preparing Application for Spring
A Vaadin application that uses Spring must have a file named applicationContext.xml in the WEB-INF directory. Using Spring Initializr automatically generates a suitable file, but if you configure Vaadin Spring manually, you can follow the model below.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<!-- Configuration object -->
<bean class="com.example.myapp.MySpringUI.MyConfiguration" />
<!-- Location for automatically scanned beans -->
<context:component-scan
base-package="com.example.myapp.domain" />
</beans>
The application should not have a servlet extending VaadinServlet, as Vaadin servlet has its own SpringVaadinServlet that is deployed automatically. If you need multiple servlets or need to customize the Vaadin Spring servlet, see Deploying Spring UIs and Servlets.
You can configure managed beans explicitly in the file, or configure them to be scanned using the annotations, which is the preferred way described in this section.
Injecting a UI with @SpringUI
Vaadin Spring offers an easier way to instantiate UIs and to define the URL mapping for them than the usual ways described in "Deploying an Application". It is also needed for enabling Spring features in the UI. To define a UI class that should be instantiated for a given URL, you simply need to annotate the class with @SpringUI. It takes an optional path as parameter.
@SpringUI(path="/myniceui")
@Theme("valo")
public class MyNiceUI extends UI {
...
The path in the URL for accessing the UI consists of the context path of the application and the UI path, for example, /myapp/myniceui. Giving empty UI path maps the UI to the root of the application context, for example, /myapp.
@SpringUI
See Deploying Spring UIs and Servlets for how to handle servlet URL mapping of Spring UIs when working with multiple servlets in the same web application.
Scopes
As in programming languages, where a variable name refers to a unique object within the scope of the variable, an object has unique identity within a scope in Spring. However, instead of identifying the objects by variable names, they are identified by their type (object class) and any qualifiers they may have.
The scope of an object can be defined with an annotation to the class as follows:
@VaadinSessionScope
public class User {
...
Defining a scope in Spring is normally done with the @Scope annotation. For example, @Scope("prototype") creates a new instance every time one is requested/auto-wired. Such standard scopes can be used with some limitations. For example, Spring session and request scopes do not work in background threads and with certain push transport modes.
Vaadin Spring provides three scopes useful in Vaadin applications: a session scope, a UI scope, a view scope, all defined in the com.vaadin.spring.annotation package.
@VaadinSessionScope
The session scope is the broadest of the custom scopes defined in Vaadin Spring. Objects in the Vaadin session scope are unique in a user session, and shared between all UIs open in the session. This is the most basic scope in Vaadin applications, useful for accessing data for the user associated with the session. It is also useful when updating UIs from a background thread, as in those cases the UI access is locked on the session and also data should be in that scope.
@UIScope
UI-scoped beans are uniquely identified within a UI instance, that is, a browser window or tab. The lifecycle of UI-scoped beans is bound between the initialization and closing of a UI. Whenever you inject a bean, as long as you are within the same UI, you will get the same instance.
Annotating a Spring view (annotated with @SpringView as described later) also as @UIScoped makes the view retain the same instance when the user navigates away and back to the view.
@ViewScope
The annotation enables the view scope in a bean. The lifecycle of such a bean 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).
Views themselves are by default view-scoped, so a new instance is created every time the user navigates to the view.
View Navigation
Vaadin Spring extends the navigation framework in Vaadin, described in "Navigating in an Application". It manages Spring views with a special view provider and enables view scoping. Furthermore, Vaadin Spring provides a customized navigator class SpringNavigator that supports the scope functionality.
Preparing the UI
You can define navigation for any single-component container, component container or bean implementing ViewDisplay, as described in "Setting Up for Navigation", but typically you set up navigation for the entire UI content. The easiest way to set up navigation is to use the annotation @SpringViewDisplay on the UI (in which case the whole contents of the UI are replaced on navigation) or on any UI scoped bean implementing one of the above mentioned interfaces.
@SpringUI(path="/myspringui")
@SpringViewDisplay
public class MySpringUI extends UI {
@Override
protected void init(VaadinRequest request) {
}
}
If not using Spring Boot, auto-configuration of navigation can be enabled with the annotation @EnableVaadinNavigation on a configuration class.
The View
A view managed by Vaadin Spring only needs to have the @SpringView annotation, which registers the view in the SpringViewProvider. The annotation is also necessary to enable Spring features in the view, such as injection.
@SpringView(name=MainView.NAME)
public class MainView extends CustomComponent implements View {
public static final String NAME = "main";
...
The annotation can have the following optional paramers:
- name (optional)
-
Specifies the path by which the view can be accessed programmatically and by the URI fragment.
@SpringView(name="main")
If the view name is not given, it is derived from the class name by removing a possible "View" suffix, making it lower case, and using a dash ("-") to separate words originally denoted by capital letters. Thereby, a view class such as MyFunnyView would have name " my-funny".
It is a recommended pattern to have the view name in a static member constant in the view class, as was done in the example previously, so that the name can be referred to safely.
You can also navigate to a view with a URI fragment such as #!myview/someparameter or programmatically with:
getUI().getNavigator().navigateTo("myview/someparameter");
The enter() method of the view gets the URI fragment as parameter as is and can interpret it in any application-defined way.
- uis
-
If the application has multiple UIs that use SpringViewProvider, you can use this parameter to specify which UIs can show the view.
@SpringView(name="myview", uis={MySpringUI.class})
If the list contains UI.class, the view is available to all UIs.
@SpringView(name="myview", uis={UI.class})
In the following, we have a login view that accesses a session-scoped user object. Here, we use a constant to define the view name, so that we can use the constant when navigating to it.
@SpringView(name=LoginView.NAME)
public class LoginView extends CustomComponent
implements View {
public final static String NAME = "";
// Here we inject to the constructor and actually do
// not store the injected object to use it later
@Autowired
public LoginView(User user) {
VerticalLayout layout = new VerticalLayout();
// An input field for editing injected data
BeanItem<User> item = new BeanItem<User>(user);
TextField username = new TextField("User name",
item.getItemProperty("name"));
username.setNullRepresentation("");
layout.addComponent(username);
// Login button (authentication omitted) / Java 8
layout.addComponent(new Button("Login", e ->
getUI().getNavigator().
navigateTo(MainView.VIEWNAME)));
setCompositionRoot(layout);
}
@Override
public void enter(ViewChangeEvent event) {}
}
You could now navigate to the view from any other view in the UI with:
getUI().getNavigator().navigateTo(LoginView.VIEWNAME);
Access Control
Access control for views can be implemented by registering beans implementing ViewAccessControl or ViewInstanceAccessControl, which can restrict access to the view either before or after a view instance is created.
Integration with authorization solutions, such as Spring Security, is provided by additional unofficial add-ons on top of Vaadin Spring.
Access Denied View
If access to a view is denied by an access control bean, the access denied view is shown for it. For non-existing views, the error view is shown. You can set up an "Access Denied" view that is shown if the access is denied with setAccessDeniedViewClass() in SpringViewProvider, and an error view with setErrorView() in SpringNavigator. The same view can also be used both as an access denied view and as an error view to hide the existence of views the user is not allowed to access.
@Autowired
SpringViewProvider viewProvider;
@Autowired
SpringNavigator navigator;
@Override
protected void init(VaadinRequest request) {
// Set up access denied view
viewProvider.setAccessDeniedViewClass(
MyAccessDeniedView.class);
// Set up error view
navigator.setErrorView(MyErrorView.class);
Note that the error view can also be a class with which an error view bean is found. In this case, the error view must be UI scoped.
Deploying Spring UIs and Servlets
Vaadin Spring hooks into Vaadin framework by using a special SpringVaadinServlet. As described earlier, you do not need to map an URL path to a UI, as it is handled by Vaadin Spring. However, in the following, we go through some cases where you need to customize the servlet or use Spring with non-Spring servlets and UIs in a web application.
Custom Servlets
When customizing the Vaadin servlet, as outlined in "Vaadin Servlet, Portlet, and Service", you simply need to extend com.vaadin.spring.server.SpringVaadinServlet instead of com.vaadin.servlet.VaadinServlet.
@WebServlet(value = "/*", asyncSupported = true)
public class MySpringServlet extends SpringVaadinServlet {
}
The custom servlet must not have @VaadinServletConfiguration, as you would normally with a Vaadin servlet, as described in "Deploying an Application".
Defining Servlet Root
Spring UIs are managed by a Spring servlet ( SpringVaadinServlet), which is by default mapped to the root of the application context. For example, if the name of a Spring UI is " my-spring-ui" and application context is /myproject, the UI would by default have URL " /myproject/my-spring-ui". If you do not want to have the servlet mapped to context root, you can use a @WebServlet annotation for the servlet or a web.xml definition to map all Spring UIs to a sub-path.
For example, if we have a root UI and another:
@SpringUI(path="") // At Spring servlet root
public class MySpringRootUI extends UI {…}
@SpringUI("another")
public class AnotherUI extends UI {…}
Then define a path for the servlet by defining a custom servlet:
@WebServlet(value = "/myspringuis/*", asyncSupported = true)
public class MySpringServlet extends SpringVaadinServlet {
}
These two UIs would have URLs /myproject/myspringuis and /myproject/myspringuis/another, respectively.
You can also map the Spring servlet to another URL in servlet definition in web.xml, as described the following.
Mixing With Other Servlets
The SpringVaadinServlet is normally used as the default servlet, but if you have other servlets in the application, such as for non-Spring UIs, you need to define the Spring servlet explicitly in the web.xml. You can map the servlet to any URL path, but perhaps typically, you define it as the default servlet as follows, and map the other servlets to other URL paths:
<web-app>
…
<servlet>
<servlet-name>Default</servlet-name>
<servlet-class>
com.vaadin.spring.server.SpringVaadinServlet
</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Default</servlet-name>
<url-pattern>/myspringuis/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>Default</servlet-name>
<url-pattern>/VAADIN/*</url-pattern>
</servlet-mapping>
</web-app>
With such a setting, paths to Spring UIs would have base path /myapp/myspringuis, to which the (optional) UI path would be appended. The /VAADIN/* only needs to be mapped to the servlet if there are no other Vaadin servlets.