Sven Ruppert

Dependency Injection with CDI

In this tutorial, we will learn how to start with Dependency Injection based on CDI in a Vaadin application.

Download base project

This tutorial uses the flow-helloworld-maven-meecrowave project as a base. Read more about it here You can find the latest version of the source code for this tutorial @github

Preparations for this tutorial

In this tutorial, we are using the Vaadin Flow CDI add-on from the official Vaadin Directory. You can find the add-on here. To use the add-on the following repository and dependency must be added to the pom.xml Replace the placeholder ${version} with the version you will find at the repository.

<repository>
   <id>vaadin-addons</id>
   <url>http://maven.vaadin.com/vaadin-addons</url>
</repository>
<dependency>
   <groupId>com.vaadin</groupId>
   <artifactId>vaadin-cdi</artifactId>
   <version>${version}</version>
</dependency>

The application

The base project has a counter that is incremented with every button click. The state of this counter is part of the UI. This will be changed now. To extract the state from the UI a service with the name Counter is created. This service is responsible for holding the state and for offering a method to manipulate the state.

public class Counter {

  private int counter = 0;

  public int inc() {
    counter++;
    return counter;
  }
}

The class VaadinApp will now be changed to use the new implementation.

@Route("")
public class VaadinApp extends Composite<Div> implements HasLogger {

  private final Button         btnClickMe   = new Button("click me");
  private final Span           lbClickCount = new Span("0");
  private final VerticalLayout layout       = new VerticalLayout(btnClickMe, lbClickCount);

  //private int clickcount = 0;
  private Counter clickcounter = new Counter();

  public VaadinApp() {
    btnClickMe.addClickListener(event -> {
      String value = valueOf(clickcounter.inc());
      lbClickCount.setText(value);
    });
    logger().info("setting now the main ui content..");
    getContent().add(layout);
  }
}

To use CDI to manage the instance of the Counter service, we will add the Annotation @Inject to the definition of the class attribute, and at the same time, we are removing the part that is responsible for creating the instance. = new Counter()

To activate CDI we need a file called beans.xml in the folder resources/META-INF. An empty file would be enough to activate CDI, but to make sure we are only enabling CDI for a subset of classes, the attribute bean-discovery-mode is set to annotated. Based on this, the class Counter needs an annotation to describe the life-cycle-behavior. In this example, @ApplicationScoped is used.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                           http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd"
       version="2.0"
       bean-discovery-mode="annotated">
</beans>
@ApplicationScoped
public class Counter {

  private int counter = 0;

  public int inc() {
    counter++;
    return counter;
  }
}

Now we can start the servlet container and try to use the new CDI version of our example.

Router components

Classes with a @Route annotation or that implement the RouteLayout and hasErrorParameter interfaces are managed Beans as well. This will allow you to use @Inject in these classes as well.

Have in mind, that the class of a custom UI is not a managed Bean.

Custom Scopes

Especially for Vaadin Apps, a few scopes are available additionally.

The Scopes are:

  • @VaadinServiceScoped, The lifecycle of the service is the same as the lifecycle of its Vaadin servlet.

  • @VaadinSessionScoped, The same bean instance will be used within the whole Vaadin session.

  • @UIScoped (for components), @NormalUIScoped (for all other beans)

  • @RouteScoped (for components), @NormalRouteScoped (for all other beans)

RouterScope

RouterScope is a bit more complex. This annotation is used in conjunction with the annotation @RouteScopeOwner. The usage of this annotation is redundant. It is a CDI qualifier, so you have to define it both on the bean, and on the injection point. With this annotation (@RouteScopeOwner) you will bind the Route including parent-/child-route constructions to bean implementations.

Service interfaces

Vaadin Flow will provide a few interfaces to give you the possibility to customize Vaadin Services.

  • I18NProvider (for more info read this)

  • Instantiator

  • SystemMessagesProvider

  • ErrorHandler

The annotation @VaadinServiceEnabled can be used to bind your implementation to these services and activate them. As an example implementation of a SystemMessagesProvider is shown.

@VaadinServiceEnabled
@VaadinServiceScoped
public class TestSystemMessagesProvider implements SystemMessagesProvider {

    @Override
    public SystemMessages getSystemMessages(SystemMessagesInfo systemMessagesInfo) {
        CustomizedSystemMessages messages = new CustomizedSystemMessages();
        messages.setInternalErrorMessage("Sorry, something went wrong :(");
        return messages;
    }
}

Next

In this tutorial, we learned how to activate CDI with the official add-on you can find in the directory. To learn more in practice check out the latest source code @github.

The documentation about CDI is available here

Vaadin is an open-source framework offering the fastest way to build web apps on Java backends
GET STARTED

Comments (0)