Vaadin CDI Contexts
@VaadinServiceScoped@VaadinSessionScoped@UIScoped,@NormalUIScoped@RouteScoped,@NormalRouteScoped
You can find a summary about contexts at Vaadin Spring Scopes. The concept is the same.
In addition to standard CDI contexts the CDI add-on introduces some new contexts.
Most scopes are normal scopes in CDI. It means a call to a managed bean
is delegated by a client proxy to the active instance. The active instance is provided by the context.
Vaadin component hierarchy does not work properly with CDI client proxies, so as a precaution the Vaadin CDI
plugin will not deploy if any such beans are discovered.
Normal scopes introduced by this add-on are @VaadinServiceScoped, @VaadinSessionScoped, @NormalUIScoped, @NormalRouteScoped.
Any scope that is not a normal scope is called a pseudo-scope.
Scopes of this kind are standard @Dependent, and @Singleton. Additionally @UIScoped, and @RouteScoped introduced by this add-on.
Injection of a pseudo-scoped bean will create a direct reference to the object.
There are some limitations when not using proxies. Circular referencing (that
is, injecting A to B and B to A) will not work.
Injecting into a larger scope will bind the instance
from the currently active smaller scope, and will ignore smaller scope change.
For example a @UIScoped bean after being injected into session scope will point to the same
instance ( even its UI is closed ) regardless of current UI.
Vaadin contexts are usable inside the UI.access method with any push transport.
|
Note
| Please refer to Asynchronous Updates about push. |
An incoming websocket message does not count as a request in CDI.
Need an http request to have request, session, and conversation context.
So you should use WEBSOCKET_XHR (it is the default), or LONG_POLLING
transport, otherwise you lost these standard contexts.
In background threads contexts depending on http request are not active regardless of push.
@VaadinServiceScoped
@VaadinServiceScoped context manages the beans during Vaadin Service lifecycle.
The lifecycle of the service is the same as the lifecycle of its Vaadin servlet.
|
Note
| Please refer to Vaadin Servlet and Service section of Application Lifecycle tutorial for details about the Vaadin Service. |
For beans automatically picked up by VaadinService you need the @VaadinServiceEnabled annotation.
It should be used together with @VaadinServiceScoped,
see Vaadin service interfaces as a CDI bean for details.
@VaadinSessionScoped
@VaadinSessionScoped context manages the beans during Vaadin Session lifecycle.
It means that the same bean instance will be used within the whole Vaadin session.
|
Note
| Please refer to User Session section of Application Lifecycle tutorial for details about the Vaadin Session lifecycle. |
Source code
Java
@Route("")
public class MainLayout extends Div {
@Inject
public MainLayout(SessionService bean){
setText(bean.getText());
}
}
@Route("editor")
public class Editor extends Div {
@Inject
public Editor(SessionService bean){
setText(bean.getText());
}
}
@VaadinSessionScoped
public class SessionService {
private String uid = UUID.randomUUID().toString();
public String getText(){
return "session "+uid;
}
}In this example the same instance of SessionService will be used as long as
we access the application from the same Vaadin session since it’s session scoped.
E.g. if you open the root target in one tab and the editor target in another tab,
then the shown text will be the same for both. It happens because the session
is the same even though the tabs (and UI instances) are different.
@UIScoped, @NormalUIScoped
The @UIScoped, and @NormalUIScoped context manages the beans during the UI lifecycle. Similar to the example above
the UIService will be the same while we are in the same UI since it’s ui scoped now.
For components use @UIScoped, for other beans you can use @NormalUIScoped.
|
Note
|
Please refer to Loading a UI
section of Application Lifecycle tutorial
for details about the UI lifecycle.
|
Source code
Java
@Route("")
public class MainLayout extends Div {
@Inject
public MainLayout(UIService bean){
setText(bean.getText());
}
}
@Route("editor")
public class Editor extends Div {
@Inject
public Editor(UIService bean){
setText(bean.getText());
}
}
@NormalUIScoped
public class UIService {
private String uid = UUID.randomUUID().toString();
public String getText(){
return "ui " + uid;
}
}Now if you open two browser tabs, the text in these will be different since the UI instances
are different. But if you navigate to the Editor instance via the router (or the UI instance which
delegates navigation to the router) then the text will be the same.
Source code
Java
public void edit() {
getUI().get().navigate("editor");
}So inside the same UI instance the same bean instance with @UIScoped, or @NormalUIScoped is used.
@RouteScoped, @NormalRouteScoped
@RouteScoped, and @NormalRouteScoped context’s lifecycle on its own is same as UI context’s.
Together with the concept of @RouteScopeOwner it can be used
to bind beans to router components (@Route, RouteLayout, HasErrorParameter).
Until owner remains in the route chain, all beans owned by it remain in the scope.
For Vaadin components use @RouteScoped, for other beans you can use @NormalRouteScoped.
|
Note
| Please refer to Defining Routes With @Route, and Router Layouts and Nested Router Targets about the details of route targets, layouts, and chain. |
Source code
Java
@Route("")
@RoutePrefix("parent")
public class ParentView extends Div implements RouterLayout {
@Inject
public ParentView(@RouteScopeOwner(ParentView.class) RouteService routeService) {
setText(routeService.getText());
}
}
@Route(value = "child-a", layout = ParentView.class)
public class ChildAView extends Div {
@Inject
public ChildAView(@RouteScopeOwner(ParentView.class) RouteService routeService) {
setText(routeService.getText());
}
}
@Route(value = "child-b", layout = ParentView.class)
public class ChildBView extends Div {
@Inject
public ChildBView(@RouteScopeOwner(ParentView.class) RouteService routeService) {
setText(routeService.getText());
}
}
@NormalRouteScoped
@RouteScopeOwner(ParentView.class)
public class RouteService {
private String uid = UUID.randomUUID().toString();
public String getText() {
return "ui " + uid;
}
}In this example ParentView, ChildAView, and ChildBView ( paths are /parent, /parent/child-a, and /parent/child-b) use the same RouteService instance,
while you navigate between them. After navigating out of ParentView, RouteService is destroyed too.
|
Note
|
As you can see @RouteScopeOwner is redundant.
It is a CDI qualifier, so you have to define it both on the bean, and on the injection point.
|
Route components can be @RouteScoped too.
In this case @RouteScopeOwner should point to a parent layout.
Omitting it means owner is the class itself.
Source code
Java
@Route("scoped")
@RouteScoped
public class ScopedView extends Div {
private void onMessage(@Observes(notifyObserver = IF_EXISTS) MessageEvent message) {
setText(message.getText());
}
}In this example message is delivered to the ScopedView instance we already navigated to.
If we are on an other view, there is no instance of this bean, and the message is not delivered to it.