📜 ⬆️ ⬇️

Add dependencies to CDI. Part 1

After the article on how to get started with CDI in your environment and a few tips on how to integrate CDI into an existing Java EE 6 application , I want to talk about dependency injection. Yes, about a simple deployment or how to embed one bin into another . In this series of three articles ( Part 2 , Part 3 ) you will see that there are many different ways: let's start with the simplest - the usual introduction.


COFFEE_BEANS


Default implementation


The easiest way to implement ... simple. You have something , and you embed something into it. Why do I use the word something ? Because before Java EE 5, you could only implement resources (EntityManager, Datasource, JMS factories ...) into certain components (EJBs and servlets). With CDI, you can embed almost anything into anything .


For this article, I used the following software:



To illustrate the implementation, I will use the same example as in previous articles and in my book Java EE 6 (the same example Antonio uses in the book Beginning Java EE 7 [ translation ] - note. Lane).


CDI_CLASS_DIAGRAM


The class diagram shows the following components:



As you can see, with the exception of EntityManager, which is implemented using @PersistenceContext , all other components are embedded with the @Inject annotation. Here are a few lines of ItemEJB code that receive a link to the EntityManager:


 @Stateless public class ItemEJB { @PersistenceContext(unitName = "cdiPU") private EntityManager em; ... } 

ItemServlet and ItemRestService very similar, since both are embedded with links to ItemEJB and IsbnGenerator :


 @WebServlet(urlPatterns = "/itemServlet") public class ItemServlet extends HttpServlet { @Inject private IsbnGenerator numberGenerator; @Inject private ItemEJB itemEJB; ... } 

IsbnGenerator is the most common POJO. It is not inherited from anyone and is not annotated in any way:


 public class IsbnGenerator { public String generateNumber () { return "13-84356-" + Math.abs(new Random().nextInt()); } } 

In all these cases, there is only one implementation to choose from (there is only one ItemEJB and one IsbnGenerator ). If you have only one implementation, CDI will be able to implement it. Next we talk about the default implementation .


Actually code:


 @Inject IsbnGenerator numberGenerator 

could be written like this:


 @Inject @Default IsbnGenerator numberGenerator 

@Default is a built-in specifier that informs the CDI that the implementation of a bin must be implemented by default. If you define a bin without a specifier, it will be automatically specified by @Default . The following code is identical to the previous one:


 @WebServlet(urlPatterns = "/itemServlet") public class ItemServlet extends HttpServlet { @Inject @Default private IsbnGenerator numberGenerator; ... } @Default public class IsbnGenerator { public String generateNumber () { return "13-84356-" + Math.abs(new Random().nextInt()); } } 

If for implementation you have only one IsbnGenerator implementation, then the default handler will work and an ordinary @Inject will do its job. But sometimes it is necessary to choose between several implementations, then the specifier comes into play.


In this article, I use the term bin , but to be more precise, I have to say managed bin (for example, bin, controlled by CDI ). ManagedBeans were introduced in Java EE 6.

Ambiguous implementations and qualifiers


For some type of bins, there may be several bins that implement this type . For example, our application can have two implementations of the NumberGenerator interface: IsbnGenerator , which generates a 13-digit number and IssnGenerator , which generates an 8-digit number. The component that needs to generate a 13-digit number must somehow distinguish between two implementations. One option is to explicitly specify the class ( IsbnGenerator ), but then a hard relationship is created between the component and the implementation. Another option is to describe the implementation of a suitable bean in an external XML configuration. CDI uses annotation specifiers to obtain strong typing and weak connectivity.


In this article, I use an introduction through a class field (attribute), but with CDI you can also use an introduction through setters or constructors.

Suppose for some purpose the ItemServlet creates books with a 13-digit ISBN number, and an ItemRestService with an 8-digit ISSN number. In both classes ( ItemServlet and ItemRestService ) there are embedded links to the same interface NumberGenerator , but which implementation will be used as a result? You do not know? CDI also does not know, and you will receive an error message:


 Ambiguous dependencies for type [NumberGenerator] with qualifiers [@Default] at injection point [[field] @Inject private ItemRestService.numberGenerator].  Possible dependencies [[Managed Bean [class IsbnGenerator] with qualifiers [@Any @Default], Managed Bean [class IssnGenerator] with qualifiers [@Any @Default]]].

This means that you need to remove the ambiguity and specify the CDI, which bin and where it should be implemented. If you used Spring before, the first thing that comes to your mind is " let's use beans.xml ". But, as explained in this post , " beans.xml is not the XML where the beans are defined ." With CDI, you must use qualifiers (annotations).


There are three specifiers built into the CDI:



A qualifier is a semantic construction that associates a type with some of its implementation. For example, you could enter a qualifier to represent a 13-digit number generator or an 8-digit number generator . In Java, specifiers are represented by annotations defined as @Target({FIELD, TYPE, METHOD}) and @Retention(RUNTIME) . They are declared with an indication of the @javax.inject.Qualifier meta-annotation as follows:


 @Qualifier @Retention(RUNTIME) @Target({FIELD, TYPE, METHOD}) public @interface EightDigits { } @Qualifier @Retention(RUNTIME) @Target({FIELD, TYPE, METHOD}) public @interface ThirteenDigits { } 

As you can see, I just very simply defined two specifiers. How can I use them now? Let's look at the chart:


CDI_CLASS_DIAGRAM_QUALIFIER


First of all, the specifier needs to annotate the corresponding implementation. As you can see, @ThirteenDigit annotates the IsbnGenerator , and @EightDigit IssnGenerator :


 @EightDigits public class IssnGenerator implements NumberGenerator { public String generateNumber() { return "8-" + Math.abs(new Random().nextInt()); } } @ThirteenDigits public class IsbnGenerator implements NumberGenerator { public String generateNumber() { return "13-84356-" + Math.abs(new Random().nextInt()); } } 

Then the components in which the references to the NumberGenerator interface are NumberGenerator must be implemented as follows:


 @WebServlet(urlPatterns = "/itemServlet") public class ItemServlet extends HttpServlet { @Inject @ThirteenDigits private NumberGenerator numberGenerator; ... } @Path("/items") @ManagedBean public class ItemRestService { @Inject @EightDigits private NumberGenerator numberGenerator; ... } 

You do not need external configuration. This is why CDI prescribes the use of strong typing. You can rename your implementations as you like, the deployment points will not change (this is a weak connectivity). Note also that a bin can be declared by several specifiers. As you can see, CDI is an elegant way to have type-safe dependency injection. But if you start creating annotations every time you need to embed something, in the end your code will be hard to read. In this case, you will be helped by transfers.


Qualifiers with enumerations


Every time you need to choose between implementations, you create an annotation. Then, if you need an additional 2-digit number generator or a 10-digit number generator , you are creating more and more annotations. It looks like we are moving from hell xml to annotation hell! One way to avoid breeding them is to use enums as follows:


 @WebServlet(urlPatterns = "/itemServlet") public class ItemServlet extends HttpServlet { @Inject @NumberOfDigits(Digits.THIRTEEN) private NumberGenerator numberGenerator; ... } @Path("/items") @ManagedBean public class ItemRestService { @Inject @NumberOfDigits(Digits.EIGHT) private NumberGenerator numberGenerator; ... } @NumberOfDigits(Digits.THIRTEEN) public class IsbnGenerator implements NumberGenerator { public String generateNumber() { return "13-84356-" + Math.abs(new Random().nextInt()); } } @NumberOfDigits(Digits.EIGHT) public class IssnGenerator implements NumberGenerator { public String generateNumber() { return "8-" + Math.abs(new Random().nextInt()); } } 

As you can see, I got rid of the @ThirteenDigits and @EightDigits and use the single @NumberOfDigit , in which the enumeration is used as the annotation value. Here is the code you should write:


 @Qualifier @Retention(RUNTIME) @Target({FIELD, TYPE, METHOD}) public @interface NumberOfDigits { Digits value(); } public enum Digits { TWO, EIGHT, TEN, THIRTEEN } 

Conclusion


I don't know about you, but I love CDI. I really like the way CDI binds the beans to plain Java and without XML . Yes, I admit that not everything is so beautiful. The first thing I see is the proliferation of the number of annotations in your code. Thanks to the listings it can be limited. The second is support in the IDE . Your IDE should know that:


 @Inject @NumberOfDigits(Digits.EIGHT) NumberGenerator numberGenerator 

refers to the IssnGenerator . As for the support in the IDE, I can not say that I deeply understood this topic. I use Intellij IDEA, and in it CDI support is simply amazing. I can move between bins without thinking about their implementation. I suppose NetBeans has some kind of support ... but I’m wondering if it is in Eclipse; o) (article published in 2011. - approx. Lane)


In the next article I will look at the various points of implementation .


Source


Download the code and tell us what you think about it.


')

Source: https://habr.com/ru/post/301636/


All Articles