📜 ⬆️ ⬇️

Add dependencies to CDI. Part 2

This is the second post about dependency injection in CDI ( Part 1 ) after our talk about how to get started with CDI in your environment and how to integrate CDI into an existing Java EE 6 application . In this post I want to talk about the different points of introduction into CDI: the field , the constructor and the setter . For this, I will use part of the previous example: embedding the POJO of an ISBN generator into a servlet .


COFFEE_BEANS


Introduction through the field


In all previous examples, you have seen @Inject annotations attached to the fields (attributes) of a class.


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

As you can see in the presented code, the @Inject annotation and the specifier (here @ThirteenDigits ) marked the attribute. But, as in many other dependency injection frameworks, you can embed through a constructor or a setter in CDI.


Implementation through the designer


Instead of attributes, you can add @Inject annotation to the constructor. If you need a specific implementation, then you can mark the constructor parameter with a qualifier:


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

As you can see, in this case, the annotation @Inject not the class attribute, but the constructor. On the other hand, @ThirteenDigits marks not the designer, but its numberGenerator parameter (which is logical). If you want, you can mix the deployment through the field and through the constructor (below I use the implementation through the constructor and the attribute for an EJB):


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

But there is a rule: you can have only one designer with the implementation . The container performs the deployment, not you (you can, of course, call the constructor in a managed environment, but it will not work as you expect). And there is only one bean constructor that allows the container to correctly embed all dependencies. The following code is incorrect :


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

If you have more than one constructor in a bin, this is what you get (the code and the error message are, of course, Weld specific)


 WELD-000812 Cannot determine the ItemServlet class.  Possible constructors [[constructor] @Inject public ItemServlet (NumberGenerator, ItemEJB), [constructor] @Inject public ItemServlet (NumberGenerator)]

Yes, it is syntactically permissible to perform an introduction through a field and a constructor at the same time, but there is no point in this:


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

Implementation through setter


There is another way - to use the implementation through the setter, which looks like an implementation through the designer. @Inject you mark the setter itself, and its specifiers with the specifiers:


 @WebServlet(urlPatterns = "/itemServlet") public class ItemServlet extends HttpServlet { private NumberGenerator numberGenerator; private ItemEJB itemEJB; @Inject public void setNumberGenerator(@ThirteenDigits NumberGenerator numberGenerator) { this.numberGenerator = numberGenerator; } @Inject public void setItemEJB(ItemEJB itemEJB) { this.itemEJB = itemEJB; } ... } 

When you use an implementation through a constructor or setter, you need to specify the arguments. Therefore, make sure that you have correctly declared @Target(java.lang.annotation.ElementType.PARAMETER) :


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

Conclusion


You may have one question (and I also asked Pete Muir ): when should I use this or that point of introduction? But there is no technical answer to this question, this is a matter of personal taste. In a managed environment, only the container performs the deployment, and all it needs is the correct deployment points. However, in the case of implementation through a constructor or setter, if necessary, you can add some kind of logic (which is impossible when implementing through attributes). But it seems that implementation through setters was added, rather, for backward compatibility with already created Java Beans .


In the next article I will talk about the producers .


Source


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


')

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


All Articles