Contexts
in Spring are services that manage the lifecycle of objects and handle their injection. A context refers to a situation in which an instance is used with a unique identity. These objects are essentially "singletons" in the context.
Scopes
are narrower than contexts. While conventional singletons are application-wide, objects managed by a Spring container are "singletons" in a narrower scope. Examples include a user session, a particular UI instance associated with the session, or even a single request. A scope defines the lifecycle of the object, that is, its creation, use, and destruction.
Read more about
beans and scopes in Spring
.
In most programming languages, a variable name refers to a unique object within the scope of the variable. In Spring, an object has a unique identity within a scope but, instead of identifying the object by its variable name, it’s identified by its type (object class) and qualifiers, if any.
With Spring Boot, you need to mark your Vaadin-related bean classes with the
@Component
annotation to allow them to be picked up as managed beans. Because there is also a
Component
class in Vaadin, you can use the
@SpringComponent
annotation from the Vaadin Spring add-on to avoid having to use fully qualified names. If no scope is specified in addition to the
@SpringComponent
/
@Component
annotation, the component is in
singleton scope
.
In addition to standard Spring scopes, the
Vaadin Spring add-on
introduces three additional scopes:
Routing Components are Spring-Initialized Beans
All Flow routing components (
@Route
,
RouterLayout
or
HasErrorParameter
) are initialized by Spring, even without explicitly adding the
@SpringComponent
annotation. Without any annotations, the components act like a bean in
prototype
scope. You can inject other beans to the components, but while
@PostConstruct
methods work,
@PreDestroy
methods don’t. When you want to use another scope for the components,
you need to remember to add the
@SpringComponent
annotation to get the scope applied.
The
@VaadinSessionScope
annotation maps the Spring beans to the Vaadin session lifecycle. It ensures that the same bean instance is used during the whole Vaadin session.
This example is using the
@VaadinSessionScope
annotation:
@Route("")
public class MainLayout extends Div {
public MainLayout(@Autowired SessionService bean) {
setText(bean.getText());
@Route("editor")
public class Editor extends Div {
public Editor(@Autowired SessionService bean) {
setText(bean.getText());
@SpringComponent
@VaadinSessionScope
public class SessionService {
private String uid = UUID.randomUUID().toString();
public String getText(){
return "session " + uid;
Provided you access the application from the same Vaadin session, the same instance of SessionService
is used. This is because it’s session-scoped.
If you open the root target in one browser tab, and the editor
target in another, the text in both is the same. This happens because the session is the same, even though the tabs (and UI
instances) are different.
See Application Lifecycle > User Session for more information on session handling.
The @UIScope
annotation manages the Spring beans during the UI
lifecycle.
This example is using the @UIScope
annotation:
@Route("")
public class MainLayout extends Div {
public MainLayout(@Autowired UIService bean) {
add(new Span(bean.getText()));
add(new RouterLink("Open editor", Editor.class));
@Route("editor")
public class Editor extends Div {
public Editor(@Autowired UIService bean) {
add(new Span(bean.getText()));
add(new RouterLink("Open MainLayout", MainLayout.class));
@SpringComponent
@UIScope
public class UIService {
private String uid = UUID.randomUUID().toString();
public String getText() {
return "ui " + uid;
The UIService
bean instance is the same inside the same UI
. If you open the MainLayout
view in one browser tab or window, and the Editor
view in another, the text in each is different, because the UI
instances are different.
When navigating inside the same browser tab between the MainLayout
and Editor
, the text stays the same, since the service is the same.
See Application Lifecycle > Loading a UI for more information on UIs.
Preserving UIScope Beans
Unlike with earlier Vaadin versions 7 and 8, the UI
and thus the UIScope
beans aren’t preserved when the @PreserveOnRefresh
annotation is used and the browser is refreshed. To preserve the beans on refresh, you need to use @RouteScope
instead (available since V21), as described in
the next chapter.
The @RouteScope
annotation ties the beans to the lifecycle of Vaadin Flow routing components (@Route
, RouterLayout
, HasErrorParameter
). Since there can be multiple nested levels of routing components present at once, an additional @RouteScopeOwner
qualifier annotation can be used to specify the owner routing component.
Without the owner qualifier, the owner is the currently active routing component at the time of injection. As long as the owner routing component is part of the active view chain, all beans owned by it remain in the scope.
Any routing component can be a @RouteScope
bean itself, and the owner can be any parent RouterLayout
in the route chain hierarchy.
See Defining Routes With @Route and Router Layouts and Nested Router Targets for more about route targets, route layouts, and the active route chain.
The example here is sharing a bean between two child views with the same parent layout:
@SpringComponent
@RouteScope
@RouteScopeOwner(ParentView.class)
public class RouteService {
private String uid = UUID.randomUUID().toString();
public String getText() {
return "ui " + uid;
@Route("")
@RoutePrefix("parent")
public class ParentView extends VerticalLayout
implements RouterLayout {
public ParentView(
@Autowired @RouteScopeOwner(ParentView.class)
RouteService routeService) {
add(new Span("Parent view:" + routeService.getText()),
new RouterLink("Open Child-A", ChildAView.class),
new RouterLink("Open Child-B", ChildBView.class),
new RouterLink("Open Sibling", SiblingView.class));
@Route(value = "child-a", layout = ParentView.class)
public class ChildAView extends VerticalLayout {
public ChildAView(
@Autowired @RouteScopeOwner(ParentView.class)
RouteService routeService) {
add(new Text("Child-a: " + routeService.getText()));
@Route(value = "child-b", layout = ParentView.class)
public class ChildBView extends VerticalLayout {
public ChildBView(
@Autowired @RouteScopeOwner(ParentView.class)
RouteService routeService) {
add(new Text("Child-a: " + routeService.getText()));
@Route(value = "sibling")
public class SiblingView extends VerticalLayout {
public SiblingView() {
add(new RouterLink("Open ParentView", ParentView.class),
new RouterLink("Open Child-A", ChildAView.class),
new RouterLink("Open Child-B", ChildBView.class));
The injected RouteService
bean instance is the same while the ParentView
is attached, such as when navigating between the child views.
When navigating to the SiblingView
, the ParentView
is detached. When navigating back to the ParentView
(or child views), a new RouteService
bean is created.
Caution
Injecting to Wider Scope
Injecting a "narrower" RouteScope
bean into "wider" scope, like parent layout’s RouteScope
or UIScope
, can cause problems. For example, if you store a RouteScope
bean into a UIScope
bean, the bean might become stale after navigation.
The @RouteScopeOwner
qualifier has to be placed both on top of the bean class and on the injection point of the bean. The annotation can be omitted in the injection point when the bean implementation can be resolved unambiguously by Spring (as it could be in the previous example). However, it’s recommended to have it there for better code readability.
Having an owner view class as a value in the @RouteScopeOwner
for a model/business logic bean class ties the application’s view layer to a model/business layer. It can be decoupled, for example, by splitting the bean class into an interface and its implementation class, and then using the interface in the view class and marking the concrete bean implementation class with @RouteScopeOwner
.
When the @RouteScopeOwner
annotation is omitted, the owner is the currently active route target. In nested routing hierarchies, the owner is the "leaf" / "bottom-most" routing component, that is, navigation target. The bean remains in scope for as long as the navigation target stays active (attached to the UI).
Compared to a @Scope("prototype")
bean injected to the routing component, the @RouteScope
bean without an owner has its @PreDestroy
method called when the routing component is no longer active. Using @RouteScope
without specifying an owner is a replacement for the @ViewScope
from Vaadin 7 or 8.
Model-View-Presenter
The following example is based on the model-view-presenter design pattern, for the sake of demonstration. It isn’t a best-practice example. It allows splitting different logical parts of the application, but adds a lot of boilerplate code.
This example shows @RouteScope
without owner behaves like the legacy Vaadin @ViewScope
: