⬆️ ⬇️

Add dependencies to CDI. Part 3

If you follow this blog, remember that lately I have been writing (and talking ) about CDI ( Contexts and Dependency Injection ). CDI has many aspects, but so far I have focused on how to get started with CDI in your environment and how to integrate CDI into an existing Java EE 6 application , and then focused on introducing dependencies into CDI. This is the third post about the implementation in CDI: in the first one I talked about the default implementation and specifiers, in the second about all possible implementation points (field, constructor, setter). In this post, I’ll talk about producers or " how you can implement anything and any type of safe way ."



COFFEE_BEANS



Implement only bins?



Before that, I showed you how to implement beans with the usual @Inject annotation. If you look at the example with the generation of the book number that I used earlier , you will see a servlet and a rest service in which the implementation of the NumberGenerator interface is being implemented. Thanks to the specifiers, the servlet can get an IsbnGenerator marked at the insertion point with the @ThirteenDigit , and the rest of the service will get the IssnGenerator marked with @EightDigits (see my first post). The presented class diagram demonstrates some combination of bin injection:



CDI_3_1



But as you can see, all this is the injection of beans into other bins . In CDI, can we embed only beans? Not. You can embed anything and anywhere .



Producers



Yes, you can embed anything and anywhere. All you need to do is create something that you want to implement later. For this, there are producers in CDI (an excellent implementation of the Factory pattern). With their help, you can create:



  • Class: unlimited set of bin types, its superclasses, and all interfaces it implements
  • Interface: unlimited set of bean types, expanded interfaces, and also java.lang.Object
  • Primitives and java array


So you can embed java.util.Date , java.lang.String or even an int . Start by creating and implementing some data types and primitives.



Creating data types and primitive types



One example of how to implement anything and anywhere is the introduction of data types and primitives. Well, let's try to embed String and int . For this, I need to add additional classes to our model. Prior to this, the IsbnGenerator created a random number in the format 13-124-454644-4 . This number consists of a string serving as a prefix ( 13-124 ), and an integer value - a postfix ( 4 ). The following class diagram shows the two new PrefixGenerator and PostfixGenerator that will be used to generate the number:



CDI_3_2



If we look, for example, at the PrefixGenerator code, we will see a class that itself is not marked by any specifier, unlike its methods. The getIsbnPrefix method returns a string that is specified by the @ThirteenDigits annotation. This string is created using CDI (thanks to @Produces ), which means that you can embed it using @Inject and its specifier ( @ThirteenDigits ).



 public class PrefixGenerator { @Produces @ThirteenDigits public String getIsbnPrefix() { return "13-84356"; } @Produces @EightDigits public String getIssnPrefix() { return "8"; } } 


Now look closely at the PostfixGenerator class. This code is similar to the previous one, except that it creates an int , which can then be embedded.



 public class PostfixGenerator { @Produces @ThirteenDigits public int getIsbnPostfix() { return 13; } @Produces @EightDigits public int getIssnPostfix() { return 8; } } 


And now we will change the logic of generating ISBN numbers. The IsbnGenerator bin is IsbnGenerator String and int using @Inject and @ThirteenDigits . Now you understand what strict typing in CDI means. Using this syntax ( @Inject @ThirteenDigits ), CDI knows what to insert into the String place, and what to the int place and which implementation of the NumberGenerator implementation to NumberGenerator .



 @ThirteenDigits public class IsbnGenerator implements NumberGenerator { @Inject @ThirteenDigits private String prefix; @Inject @ThirteenDigits private int postfix; public String generateNumber() { return prefix + "-" + Math.abs(new Random().nextInt()) + "-" + postfix; } } 


Embedding Java EE resources through producer fields



So, if CDI can embed anything and anywhere, if it can even embed strings and integers, then what about Java EE resources? This is another story, and the producers will help us in this. As I said earlier, everything is type-safe in CDI, so there is no way in CDI to embed a resource by its JNDI name , for example, @Inject(name="jms/OrderQueue") . The standard example is an entity manager. If you do not use CDI, then in Java EE 6 you can implement it as follows:



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


So why is it impossible to make an ordinary @Inject for an entity manager? If you remember my first post about the ambiguity of implementation, then there is the same problem. You may have several units of persistence (named with a regular string), and if you just write @Inject , then CDI will not be able to figure out which one of them needs to be implemented. Instead, you should create the entity manager first and name it somehow (if you don’t want to use @Default ):



 public class DatabaseProducer { @Produces @PersistenceContext(unitName = "cdiPU") @BookStoreDatabase private EntityManager em; } 


The DatabaseProducer class uses @PersistenceContext to implement the entity manager with the cdiPU persistence unit. It will be marked with a specifier ( @BookStoreDatabase ) and initialized, after which you can embed it in an EJB as follows:



 @Stateless public class ItemEJB { @Inject @BookStoreDatabase private EntityManager em; ... } 


Another good example is the creation of JMS factories and recipients. The @Resource allows you to get a JNDI link to a JMS factory or destination, the @Order names it, and @Produces allows you to implement it in the future:



 public class JMSResourceProducer { @Produces @Order @Resource(name = "jms/OrderConnectionFactory") private QueueConnectionFactory orderConnectionFactory; @Produces @Order @Resource(name = "jms/OrderQueue") private Queue orderQueue; } 


Now your EJB can use type-safe @Inject :



 @Stateless public class ItemEJB { @Inject @Order private QueueConnectionFactory orderConnectionFactory; @Inject @Order private Queue orderQueue; ... } 


Creating Java EE Resources Using Producer Methods



The examples considered are quite simple: we create a field and then we can implement it. This is called a producing field. But sometimes you need a more complex business logic to create a bean, and we will call this the producer method. Let's look at another example. When you need to send a JMS message, you always embed a JMS factory and destination (a queue or topic), create a connection, then a session, and so on, until you send a message. Since this code is often repeated and may contain errors, why not put it somewhere in a separate class and create a session with it and other components necessary to send a message? This class might look like this:



 public class JMSResourceProducer { @Resource(name = "jms/OrderConnectionFactory") private QueueConnectionFactory orderConnectionFactory; @Produces @Order @Resource(name = "jms/OrderQueue") private Queue orderQueue; @Produces @Order public QueueConnection createOrderConnection() throws JMSException { return orderConnectionFactory.createQueueConnection(); } @Produces @Order public QueueSession createOrderSession(@Order QueueConnection conn) throws JMSException { return conn.createQueueSession(true, Session.AUTO_ACKNOWLEDGE); } } 


First, the class uses @Resource to get references to QueueConnectionFactory and Queue . For the same reasons that I described above, you may have several JMS factories and recipients, and you must distinguish them by their JNDI name. And since CDI does not allow you to specify a name at the deployment point, you must use @Resource instead of @Inject . The createOrderConnection method uses a QueueConnectionFactory to create a QueueConnection and then QueueConnection it (simultaneously calling it @Order ). If you look closely at the createOrderSession method, you will see that its parameter is the one created earlier for implementing the QueueConnection and is used to create a QueueSession . As a result, it is enough just to embed a JMS session into an external component without creating it:



 @Stateless public class ItemEJB { @Inject @Order private QueueSession session; @Inject @Order private Queue orderQueue; private void sendOrder(Book book) throws Exception { QueueSender sender = session.createSender(orderQueue); TextMessage message = session.createTextMessage(); message.setText(marshall(book)); sender.send(message); } ... } 


Creating and ... recycling



You can say: “Ok, I have an external class for rough work and creating a connection and session ... but who will close them? ” Indeed, someone must release these resources by closing them. And at this moment CDI shows a little more magic: @Disposes .



 public class JMSResourceProducer { @Resource(name = "jms/OrderConnectionFactory") private QueueConnectionFactory orderConnectionFactory; @Produces @Order @Resource(name = "jms/OrderQueue") private Queue orderQueue; @Produces @Order public QueueConnection createOrderConnection() throws JMSException { return orderConnectionFactory.createQueueConnection(); } @Produces @Order public QueueSession createOrderSession(@Order QueueConnection conn) throws JMSException { return conn.createQueueSession(true, Session.AUTO_ACKNOWLEDGE); } public void closeOrderSession(@Disposes @Order QueueConnection conn) throws JMSException { conn.close(); } public void closeOrderSession(@Disposes @Order QueueSession session) throws JMSException { session.close(); } } 


In order to ask CDI to close a resource, you simply need to define a method similar to the one that created this resource ( createOrderSession(@Order QueueConnection conn) creates a session, and closeOrderSession(@Order QueueConnection conn) to close it) and add the @Disposes annotation. CDI will dispose of resources in the correct order for you (first session, then connection). I did not mention this, but CDI creates and utilizes resources depending on their scope (resquest, session, application, conversation ...). But more about that another time. (Information is in Beginning Java EE 7 [ translation ] - approx. Lane.)



Conclusion



As you understood from my previous posts ( part 1 , part 2 ), CDI is about dependency injection. Until now, I have shown how to implement beans, but now you know how to embed anything (strings, primitives, entity managers, JMS factories ...) anywhere (POJO, servlets, EJB ...). You just need to create what you want to implement (using either producing fields or producing methods).



Source



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



')

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



All Articles