Introduction
So you’re keen to get started writing your first bean? Or perhaps you’re skeptical, wondering what kinds of hoops the CDI specification will make you jump through! The good news is that you’ve probably already written and used hundreds, perhaps thousands of beans. CDI just makes it easier to actually use them to build an application!
What is a bean?
A bean is exactly what you think it is. Only now, it has a true identity in the container environment.
Starting with Java EE 6 (now Jakarta) there was a common definition of beans through Managed Beans specification. Managed Beans were defined as container-managed objects with minimal programming restrictions, otherwise known by the acronym POJO (Plain Old Java Object). They support a small set of basic services, such as resource injection, lifecycle callbacks and interceptors. CDI/Weld builds on this basic model and clearly defines a uniform concept of a bean and a lightweight component model that’s aligned across the Jakarta EE platform, MicroProfile specification and more.
With very few exceptions, almost every concrete Java class that has a
constructor with no parameters (or a constructor designated with the
annotation @Inject
) is a bean; including EJB sessions beans. If you’ve already got some session beans lying around, they’re already beans—you won’t need any additional special metadata.
CDI container manages the lifecycle of your beans from creation to destruction. It also controls their association to designated context, injection into beans, bean availability in EL expressions, interception and decoration, specialization with qualifiers and more. Many of these functionalities are working automatically, for some you may need to add an annotation or two.
But enough talking, let’s see how to create your first bean that actually uses CDI.
Getting our feet wet
Suppose that we have two existing Java classes that we’ve been using for years in various applications. The first class parses a string into a list of sentences:
public class SentenceParser {
public List<String> parse(String text) { ... }
}
The second existing class is a stateless session bean front-end for an external system that is able to translate sentences from one language to another:
@Stateless
public class SentenceTranslator implements Translator {
public String translate(String sentence) { ... }
}
Where Translator
is the EJB local interface:
@Local
public interface Translator {
public String translate(String sentence);
}
Unfortunately, we don’t have a class that translates whole text documents. So let’s write a bean for this job:
public class TextTranslator {
private SentenceParser sentenceParser;
private Translator sentenceTranslator;
@Inject
TextTranslator(SentenceParser sentenceParser, Translator sentenceTranslator) {
this.sentenceParser = sentenceParser;
this.sentenceTranslator = sentenceTranslator;
}
public String translate(String text) {
StringBuilder sb = new StringBuilder();
for (String sentence: sentenceParser.parse(text)) {
sb.append(sentenceTranslator.translate(sentence));
}
return sb.toString();
}
}
But wait! TextTranslator
does not have a constructor with no
parameters! Is it still a bean? If you remember, a class that does not
have a constructor with no parameters can still be a bean if it has a
constructor annotated @Inject
.
As you’ve guessed, the @Inject
annotation has something to do with
dependency injection! @Inject
may be applied to a constructor or
method of a bean, and tells the container to call that constructor or
method when instantiating the bean. The container will inject other
beans into the parameters of the constructor or method.
We may obtain an instance of TextTranslator
by injecting it into a
constructor, method or field of a bean, or a field or method of a Java
EE component class such as a servlet. The container chooses the object
to be injected based on the type of the injection point, not the name of
the field, method or parameter.
Let’s create a UI controller bean that uses field injection to obtain an
instance of the TextTranslator
, translating the text entered by a
user:
@Named @RequestScoped
public class TranslateController {
@Inject TextTranslator textTranslator; // (1)
private String inputText;
private String translation;
// JSF action method, perhaps
public void translate() {
translation = textTranslator.translate(inputText);
}
public String getInputText() {
return inputText;
}
public void setInputText(String text) {
this.inputText = text;
}
public String getTranslation() {
return translation;
}
}
-
Field injection of
TextTranslator
instance
Tip
|
Notice the controller bean is request-scoped and named. Since this
combination is so common in web applications, there’s a built-in
annotation for it in CDI that we could have used as a shorthand. When
the (stereotype) annotation @Model is declared on a class, it creates
a request-scoped and named bean.
|
Alternatively, we may obtain an instance of TextTranslator
programmatically from an injected instance of Instance
, parameterized
with the bean type:
import jakarta.enterprise.inject.Instance;
import jakarta.inject.Inject;
....
@Inject Instance<TextTranslator> textTranslatorInstance;
...
public void translate() {
textTranslatorInstance.get().translate(inputText);
}
Notice that it isn’t necessary to create a getter or setter method to inject one bean into another. CDI can access an injected field directly (even if it’s private!), which sometimes helps eliminate some wasteful code. The name of the field is arbitrary. It’s the field’s type that determines what is injected.
At system initialization time, the container must validate that exactly
one bean exists which satisfies each injection point. In our example, if
no implementation of Translator
is available—if the
SentenceTranslator
EJB was not deployed—the container would inform us
of an unsatisfied dependency. If more than one implementation of
Translator
were available, the container would inform us of the
ambiguous dependency.
Before we get too deep in the details, let’s pause and examine a bean’s anatomy. What aspects of the bean are significant, and what gives it its identity? Instead of just giving examples of beans, we’re going to define what makes something a bean.