2017-09-20 10:02:36 +02:00
|
|
|
---
|
|
|
|
title: Injection And Scopes
|
|
|
|
order: 48
|
|
|
|
layout: page
|
|
|
|
---
|
|
|
|
|
2017-09-12 12:31:26 +02:00
|
|
|
[[ii-injection-and-scopes]]
|
2018-05-04 11:22:27 +02:00
|
|
|
= II - Injection and scopes
|
2017-09-12 12:31:26 +02:00
|
|
|
|
|
|
|
In this tutorial we'll take a closer look at the @CDIUI annotation and
|
|
|
|
use CDI to inject some beans to our application.
|
|
|
|
|
|
|
|
[[cdiui]]
|
|
|
|
@CDIUI
|
|
|
|
~~~~~~
|
|
|
|
|
|
|
|
The @CDIUI annotation is the way in which you let the Vaadin CDI plugin
|
|
|
|
know which UI's should be accessible to the user and how they should be
|
|
|
|
mapped. It accepts one optional String parameter indicating the UI path.
|
|
|
|
If an explicit path is not provided the class name of the UI will be
|
|
|
|
used to construct a pathname by the following convention: any trailing
|
|
|
|
"UI" will be truncated and camelcase will be converted to hyphenated
|
|
|
|
lowercase. Some examples of the convention:
|
|
|
|
|
|
|
|
....
|
|
|
|
HelloWorldUI → hello-world
|
|
|
|
ExampleUI → example
|
|
|
|
VisualEditor → visual-editor
|
|
|
|
....
|
|
|
|
|
|
|
|
Passing an empty String as the path will cause the UI to be mapped to
|
|
|
|
the root of the deployment. Most single UI applications will probably
|
|
|
|
want to do this.
|
|
|
|
|
|
|
|
[[injecting-beans]]
|
|
|
|
Injecting beans
|
|
|
|
~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
Now that the UI itself has been injected, we can use the @Inject
|
|
|
|
annotation to further inject beans to it. Let's create something for us
|
|
|
|
to actually inject.
|
|
|
|
|
|
|
|
We'll define the following interface, and an implementation for it.
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
....
|
|
|
|
package com.vaadin.cdi.tutorial;
|
|
|
|
|
|
|
|
public interface Greeting {
|
|
|
|
public String getText();
|
|
|
|
}
|
|
|
|
....
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
....
|
|
|
|
package com.vaadin.cdi.tutorial;
|
|
|
|
|
|
|
|
import java.io.Serializable;
|
|
|
|
|
|
|
|
public class SimpleGreetingImpl implements Greeting, Serializable {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String getText() {
|
|
|
|
return "Hello, World!";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
....
|
|
|
|
|
|
|
|
So far so good, now we'll inject it into our UI. You'll need to add the
|
|
|
|
CDI API as a dependency in your pom.xml. (Group id: javax.enterprise,
|
|
|
|
artefact id: cdi-api, version: 1.2)
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
....
|
|
|
|
package com.vaadin.cdi.tutorial;
|
|
|
|
|
|
|
|
import javax.inject.Inject;
|
|
|
|
|
|
|
|
import com.vaadin.annotations.Theme;
|
|
|
|
import com.vaadin.cdi.CDIUI;
|
|
|
|
import com.vaadin.server.VaadinRequest;
|
|
|
|
import com.vaadin.ui.Button;
|
|
|
|
import com.vaadin.ui.Button.ClickEvent;
|
|
|
|
import com.vaadin.ui.Label;
|
|
|
|
import com.vaadin.ui.UI;
|
|
|
|
import com.vaadin.ui.VerticalLayout;
|
|
|
|
|
|
|
|
@SuppressWarnings("serial")
|
|
|
|
@CDIUI("")
|
|
|
|
@Theme("valo")
|
|
|
|
public class HelloWorldUI extends UI {
|
|
|
|
|
|
|
|
@Inject
|
|
|
|
private Greeting greeting;
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void init(VaadinRequest request) {
|
|
|
|
final VerticalLayout layout = new VerticalLayout();
|
|
|
|
layout.setMargin(true);
|
|
|
|
setContent(layout);
|
|
|
|
|
|
|
|
Button button = new Button("Click Me");
|
|
|
|
button.addClickListener(new Button.ClickListener() {
|
|
|
|
public void buttonClick(ClickEvent event) {
|
|
|
|
layout.addComponent(new Label(greeting.getText()));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
layout.addComponent(button);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
....
|
|
|
|
|
|
|
|
Let's run that and see.
|
|
|
|
|
|
|
|
image:img/hello-world.png[Injection seems to be working]
|
|
|
|
|
|
|
|
So far so good. Suppose we want to say hello to the user by name. We'll
|
|
|
|
create a class to store our user data in. For now it's a simple POJO
|
|
|
|
just for storing the name in a single string.
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
....
|
|
|
|
package com.vaadin.cdi.tutorial;
|
|
|
|
|
|
|
|
import java.io.Serializable;
|
|
|
|
|
|
|
|
public class UserInfo implements Serializable {
|
|
|
|
private String name;
|
|
|
|
|
|
|
|
public UserInfo() {
|
|
|
|
this.name = "stranger";
|
|
|
|
}
|
|
|
|
|
|
|
|
public UserInfo(String name) {
|
|
|
|
this.name = name;
|
|
|
|
}
|
|
|
|
|
|
|
|
public String getName() {
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setName(String name) {
|
|
|
|
this.name = name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
....
|
|
|
|
|
|
|
|
We'll inject that to the UI and assign the user some name during
|
2017-10-04 10:29:34 +02:00
|
|
|
initialization (for now)
|
2017-09-12 12:31:26 +02:00
|
|
|
|
|
|
|
[source,java]
|
|
|
|
....
|
|
|
|
@Inject
|
|
|
|
private UserInfo user;
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void init(VaadinRequest request) {
|
|
|
|
...
|
|
|
|
user.setName("Ernest");
|
|
|
|
}
|
|
|
|
....
|
|
|
|
|
|
|
|
Then we'll create a new implementation of the Greeting and inject the
|
|
|
|
user there as well.
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
....
|
|
|
|
package com.vaadin.cdi.tutorial;
|
|
|
|
|
|
|
|
import javax.inject.Inject;
|
|
|
|
|
|
|
|
public class UserGreetingImpl implements Greeting {
|
|
|
|
|
|
|
|
@Inject
|
|
|
|
private UserInfo user;
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String getText() {
|
|
|
|
return "Hello, " + user.getName() + "!";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
....
|
|
|
|
|
|
|
|
Now, it would be easy to think that that's all you need but we're not
|
|
|
|
quite there. There are two issues with this that need to be addressed.
|
|
|
|
The first one will become immediately obvious when you try to deploy the
|
|
|
|
application. The deployment will fail as the injection in HelloWorldUI
|
|
|
|
is ambiguous, that is CDI doesn't know which implementation of Greeting
|
|
|
|
we want. +
|
|
|
|
There are three annotations that are useful in situations like this:
|
|
|
|
@Default, @Alternative and @Specializes. Giving a bean any of these
|
|
|
|
annotations will affect it's preference order when the CDI container is
|
|
|
|
looking for which implementation to inject. Unless otherwise specified,
|
|
|
|
beans will be considered to have the @Default annotation. Beans with the
|
|
|
|
@Alternative annotation will only be injected if that particular bean is
|
|
|
|
named in the beans.xml file (TODO: add link). Beans with the
|
|
|
|
@Specializes annotation will be considered a drop-in replacement for
|
|
|
|
it's superclass, and will be used over any implementations it extends. +
|
|
|
|
In our case, we'll want to give SimpleGreetingImpl the @Default
|
|
|
|
annotation and UserGreetingImpl the @Alternative annotation.
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
....
|
|
|
|
@Default
|
|
|
|
public class SimpleGreetingImpl implements Greeting {
|
|
|
|
....
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
....
|
|
|
|
@Alternative
|
|
|
|
public class UserGreetingImpl implements Greeting {
|
|
|
|
....
|
|
|
|
|
|
|
|
After that the application could actually be deployed. To tell CDI we'll
|
|
|
|
want to use our alternative implementation we need to create the
|
|
|
|
beans.xml in our WEB-INF folder, and add the following declaration to
|
|
|
|
it:
|
|
|
|
|
|
|
|
[source,xml]
|
|
|
|
....
|
|
|
|
<beans>
|
|
|
|
<alternatives>
|
|
|
|
<class>com.vaadin.cdi.tutorial.UserGreetingImpl</class>
|
|
|
|
</alternatives>
|
|
|
|
</beans>
|
|
|
|
....
|
|
|
|
|
|
|
|
Let's try that out:
|
|
|
|
|
|
|
|
image:img/hello-stranger.png[Something's not right]
|
|
|
|
|
|
|
|
Better, but not quite there yet. We're getting the wrong username,
|
|
|
|
despite the fact that we set the username to "Earnest" in the UI. The
|
|
|
|
problem here is the scope of the bean. If you don't specify a scope for
|
|
|
|
your bean either in the bean class itself or at the injection point,
|
|
|
|
it'll default to using the dependent scope. Every time you inject a
|
|
|
|
dependent bean you'll get a new instance, which is clearly not what we
|
|
|
|
want here. Let's go back to our UserInfo class and assign it an explicit
|
|
|
|
scope.
|
|
|
|
|
|
|
|
[source,java]
|
|
|
|
....
|
|
|
|
import com.vaadin.cdi.UIScoped;
|
|
|
|
|
|
|
|
@UIScoped
|
|
|
|
public class UserInfo {
|
|
|
|
...
|
|
|
|
....
|
|
|
|
|
|
|
|
The @UIScoped annotation is specific to Vaadin CDI. Anything injected
|
|
|
|
with that annotation will get the same instance while within the same
|
|
|
|
UI. Load a different UI and you'll get a different instance. If the
|
|
|
|
session expires or the UI is closed the instances will be cleaned up. +
|
|
|
|
Let's see if it worked.
|
|
|
|
|
|
|
|
image:img/hello-earnest.png[Something IS right]
|
|
|
|
|
|
|
|
Looks like we're making progress.
|