zoukankan      html  css  js  c++  java
  • Injection with CDI (Part I)

    官方参考:http://docs.jboss.org/weld/reference/latest/en-US/html/index.html

    https://antoniogoncalves.org/2011/04/07/injection-with-cdi-part-i/

    After writing a post about how to bootstrap CDI in your environment and giving you some tips about how to incorporate CDI in an existing Java EE 6 application, I want to talk about injection. Yes, pure injection or how to inject a bean into another one. As you’ll see in this series of articles (Part IIPart III), many different cases can happen. Let’s start with a simple one : a straight forward injection.

    Default injection

    The simplest possible injection case is… simple. You have something, and you inject something into it. Why am I using the word something ? Because until Java EE 5 we could only inject resources (EntityManager, Datasource, JMS destinations and factories…) into certain components (EJBs and Servlets). With CDI, you can inject nearly anything into anything else.

    Versions of software used for this arcticle
    Java SE 1.6.0_33
    GlassFish 3.1.2.2
    Maven 3.0.3

    To illustrate injection I’ll be using the same use case I’ve used in previous articles or in my Java EE 6 book : a CD-Book store.

    The class diagram above shows the following components :

    • Book is just an Entity with some attributes and named queries
    • ItemEJB is a stateless EJB (with no interface) doing CRUD operations on the Book thanks to the EntityManager
    • IsbnGenerator is just a POJO that generates a random ISBN number (used for the Book)
    • ItemRestService is annotated with @Path (which designates a REST web service in JAX-RS) and delegates the CRUD operation to the ItemEJB
    • ItemServlet is a Servlet that uses the ItemEJB to display all the books from the database

    As you can see, except for the EntityManager that is injected with @PersistenceContext, all the other components use @Inject. Here is a few lines of code of the ItemEJB getting a reference of the EntityManager :

    1
    2
    3
    4
    5
    6
    7
    @Stateless
    public class ItemEJB {
     
        @PersistenceContext(unitName = "cdiPU")
        private EntityManager em;
        ...
    }

    The ItemServlet and the ItemRestService are very similar as they both inject a reference of the ItemEJB and the IsbnGenerator :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @WebServlet(urlPatterns = "/itemServlet")
    public class ItemServlet extends HttpServlet {
     
        @Inject
        private IsbnGenerator numberGenerator;
     
        @Inject
        private ItemEJB itemEJB;
        ...
    }

    And the IsbnGenertor has absolutely nothing special to it as it doesn’t extends from anything nor it is annotated, it’s just a POJO :

    1
    2
    3
    4
    5
    6
    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 ItemEJB, only one IsbnGenerator). If you only have one implementation, CDI will be able to inject it. We then talk about default injection. In fact, the code :

    @Inject IsbnGenerator numberGenerator

    could have been written

    @Inject @Default IsbnGenerator numberGenerator

    @Default is a built in qualifier that informs CDI to inject the default bean implementation. If you define a bean with no qualifier, the bean automatically has the qualifier @Default. The following code is identical to the previous one.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @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 you only have one implementation of the IsbnGenerator to inject, the default behaviour applies and a straight forward @Inject does the job. But sometimes you have to choose between several implementations, that’s where qualifiers come into play.

    In this article I use the term bean, but to be more precise I should say managed bean (ie beans that are managed by CDI). ManagedBeans have been introduced in Java EE 6.

    Ambiguous injection & Qualifiers

    For a given bean type, there may be multiple beans which implement the type. For example, our application can have two implementations of the NumberGenerator interface : IsbnGenerator generates a 13 digits number and IssnGenerator a 8 digits number. A component that needs to generate a 13 digits number needs some way to distinguish between the two implementations. One approach would be to explicitly specify the class (IsbnGenerator) but that creates a hard dependence between the component and the implementation. Another approach would be to rely on external XML configuration to declare and inject the appropriate bean. CDI uses qualifiers, which are annotations, to get strong typing and loose coupling.

    In this article I’m using injection on the attribute, meaning that the @Inject annotation is on the attribute. But with CDI you can also use setter injection or constructor injection.

    Let’s say, for some reason, that the ItemServlet creates books with a 13 digits ISBN number, and the ItemRestService creates books with a 8 digits ISSN number. Both (the ItemServlet and the ItemRestService) inject a reference of the same NumberGenerator interface, which implementation will there use ? You don’t know ? CDI doesn’t know either, and this is the kind of error message you will get :

    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]]].

    That means we need to be less ambiguous and tell CDI which bean to inject where. If you come from Spring the first thing that comes to your mind is “let’s use the beans.xml file“. But as this post says, “beans.xml is not there to define beans in XML“. With CDI you use qualifiers (annotations).

    There are three built-in qualifiers in CDI :

    • @Default : If a bean does not explicitly declare a qualifier, the bean has a @Default qualifier
    • @Any : allows the application to specify qualifiers dynamically
    • @New : allows the application to obtain a new qualified bean

    A qualifier represents some semantic associated with a type that is satisfied by some implementations of the type. For example, you could introduce qualifiers to represent thirteen digits number generator or eight digits number generator. In Java qualifiers are represented by annotations defined as @Target({FIELD, TYPE, METHOD}) and @Retention(RUNTIME). It is declared by specifying the @javax.inject.Qualifier meta-annotation as follow :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @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’ve just defined two qualifiers, very easily. Right, so how do I use them now ? Better than words, a class diagram will make it clear.

    First of all, the qualifiers need to be applied on the appropriate implementation. As you can see, @ThirteenDigits is applied to IsbnGenerator and @EightDigits to @IssnGenerator :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @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 that inject a reference to the NumberGenerator interface also need to use it as follow :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    @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 don’t need external configuration, that’s why CDI is said to use strong typing. You can rename your implementations to whatever you want, the injection point will not change (that’s loose coupling). Note that a bean may declare multiple qualifiers. As you can see, CDI is an elegant way to have typesafe injection. But if you start creating annotations each time you need to inject something, your application will end up being very verbose. That’s when enumerations can help you.

    Qualifiers with enumerations

    Each time you need to choose between implementation you create an annotation. So if you need an extra two digits number generator or a ten digits number generator you create more and more annotations. Looks like we are moving from XML Hell to Annotation Hell ! One way to avoid the multiplication of annotations is to use enumeration as follow :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    @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 qualifiers and I’m using the single qualifier @NumberOfDigits which as an enumerations as a value (in my example @EightDigits). This is the code you need to write :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @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 it. I really like the way CDI wires beans together without XML, just with pure Java in a type-safe way. Ok, ok, I admit, not everything is beautiful. The first thing I see is the multiplication of annotations in your code. Thanks to enumerations, this can be limited. The other point I can see is the IDE support. Your IDE needs to be cleaver to know that :

    @Inject @NumberOfDigits(Digits.EIGHT) NumberGenerator numberGenerator

    refers to IssnGenerator. But here I’m talking without really knowing much about the topic. I use Intellij IDEA and the CDI support is just amazing. I can navigate from bean to bean without worrying about knowing the implementation. I suppose NetBeans might have some kind of support… but I wonder if Eclipse does ;o)

    The next article will cover the different available injection points, so stay tuned.

    Download

    Download the code, give it a try, and give me some feedback.

    References

      • Dependency Injection and replacing dependencies in CDI/Weld
      • TOTD #144: CDI @Produces for container-managed @Resource
      • Weld – JSR-299 Reference Implementation

      • This is the second post based on pure CDI Injection (see Part I) after having talked about how to bootstrap CDI in several environments and how to add CDI to an existing Java EE 6 application. In this post I quickly want to show the different injection points in CDI : field, constructor and setter. To illustrate these different injection points I’ll use a subset of the previous example : a servlet injecting an ISBN generator POJO :

        Field injection

        Until now, in all the previous posts, you’ve seen the @Inject annotation put on fields (attributes).

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        @WebServlet(urlPatterns = "/itemServlet")
        public class ItemServlet extends HttpServlet {
         
            @Inject
            @ThirteenDigits
            private NumberGenerator numberGenerator;
         
            @Inject
            private ItemEJB itemEJB;
            ...
        }

        As you can see in the code above, the @Inject and the qualifiers (here @ThirteenDigits) annotate an attribute. But like many other injection frameworks, in CDI you can also have constructor and setter injection.

        Constructor injection

        Instead of annotating the attributes, you can add the @Inject annotation on a constructor. If you then need to use qualifiers, you can add them to the attributes of the constructor as follow :

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        @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, the @Inject is not on the field but on the constructor. On the other hand, @ThirteenDigits doesn’t qualify the constructor but the numberGenerator parameter itself (which is logical). You can also mix field and constructor injection if you want (below I use constructor injection and attribute injection on the EJB) :

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        @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 the rule is : you can only have one constructor injection point. The container is the one doing injection, not you (you can’t invoke a constructor in a managed environment, well, you can, but it will not work as you expect). There is only one bean constructor allowed so the container can do its job of injecting the right references. The following is invalid :

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        @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 bean constuctor, this is what you get (the error code and message is Weld specific of course) :

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

        On the other hand, it is syntactically legal to have field and constructor injection at the same time (but useless) :

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        @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;
            }
            ...
        }

        Setter injection

        The other choice is to use setter injection which looks like constructor injection. You annotate the setter with @Inject and you qualify the parameters :

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        @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 constructor or setter injection you need to apply your qualifiers to a parameter. So make sure you have the right @Targets (java.lang.annotation.ElementType.PARAMETER) definition :

        1
        2
        3
        4
        5
        @Qualifier
        @Retention(RUNTIME)
        @Target({FIELD, TYPE, METHOD, PARAMETER})
        public @interface ThirteenDigits {
        }

        Conclusion


        One question you could ask (and that’s the one I asked to Pete Muir) is : “when do you use field over constructor or setter injection ?”. There is no real technical answer except that it’s your personal taste. In a managed environment, the container is the one doing all the injection work, it just needs the right injection points. However, with constructor or setter injection you can add some logic if needed (not with field injection). But it looks like setter injection was added for backward compatibility with existing Java Beans.

        The next article will cover producers, so stay tuned.

        Download

        Download the code, give it a try, and give me some feedback.

        References




      • If you follow this blog you should know that latelly I’ve been writing (and talking) about CDI (Contexts and Dependency Injection). CDI has many aspects to it but until now I’ve focused onhow to boostrap CDI in several environments, how to add CDI to an existing Java EE 6 application,  and more recently how to use injection with CDI. Actually this post is the third on CDI Injection : Part I focused on default injection and qualifiers, and Part II on all the possible injection points (field, constructor and setters). In this post I’ll explainproducers or “how you can inject anything anywhere in a type safe manner“.

        Injecting just beans ?

        Until now I’ve shown you how to inject beans with a simple @Inject. If we focus on the book number generator example I’ve been using, we have a Servlet and a RESTService being injected an implementation of the NumberGenerator interface. Thanks toqualifiers, the servlet can specifically ask to get the IsbnGenerator by specifying the @ThirteenDigit qualifier on the injection point and the rest service an IssnGenerator with a @EightDigits (check my first post). The following class diagram shows you some combinations of bean injection :

        But as you can see, all this is are beans injecting other beans. Can we just inject beans with CDI ? The answer is no, you can inject anything anywhere.

        Producers

        Yes, you can inject anything anywhere, the only thing you have to do is produce the thing you want to inject. For that, CDI hasproducers (a nice implementation of the Factory pattern) A producer exposes any sort of :

        • Class : unrestricted set of bean types, superclass, and all interfaces it implements directly or indirectly
        • Interface : unrestricted set of bean types, interfaces it extends directly or indirectly, and java.lang.Object
        • Primitive and Java array type

        So by that you can inject a java.util.Date, java.lang.String or even an int. Let’s start by producing and injecting some data types and primitives.

        Producing data types and primitives

        One example of injecting anything anywhere is the possibility to inject data types or primitives. So let’s inject a String and an int. For that I need to add extra classes to our model. Until now, the IsbnGenerator would generate a random number like 13-124-454644-4. I can say that this number is made of a String that acts like a prefix (13-124) and an int that acts like a suffix (4). The following class diagram shows the two new classes PrefixGenerator and PostfixGeneratorthat will be used by the number generators :

        If we look at the code of the PrefixGenerator for example, you can see that the class itself is not annotated by any qualifier, but the methods are. The method getIsbnPrefix returns a String that is qualified with @ThirteenDigits. This String is produced by CDI (thanks to @Produces), meaning that you can now inject it with @Inject using its qualifier (@ThirteenDigits)

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        public class PrefixGenerator {
         
            @Produces @ThirteenDigits
            public String getIsbnPrefix() {
                return "13-84356";
            }
         
            @Produces @EightDigits
            public String getIssnPrefix() {
                return "8";
            }
        }

        And now look carefully at the class  PostfixGenerator. The code is exactly the same as previously except in this case we produce an intthat can now be injected.

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        public class PostfixGenerator {
         
            @Produces @ThirteenDigits
            public int getIsbnPostfix() {
                return 13;
            }
         
            @Produces @EightDigits
            public int getIssnPostfix() {
                return 8;
            }
        }

        So now let’s change the way an ISBN number is generated. The bean IsbnGenerator now injects both a String and an int with @Inject @ThirteenDigits. You understand now what strong typing means with CDI.  Using the same syntax (@Inject @ThirteenDigits), CDI knows that it needs to inject a String, an int or an implementation of a NumberGenerator.

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        @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;
            }
        }

        Injecting Java EE resources with producer fields

        So if CDI can inject anything anywhere, if CDI can even inject Strings and integers, what about Java EE resources ? That’s another story and producers are here to help. As I’ve said before, CDI is all about type safety : CDI doesn’t like Strings, so there is no way to inject a resource by its JNDI name such as @Inject(name=”jms/OrderQueue”). A common example is the entity manager. This is how you must inject it in Java EE 6 if you don’t use CDI :

        1
        2
        3
        4
        5
        6
        7
        @Stateless
        public class ItemEJB {
         
            @PersistenceContext(unitName = "cdiPU")
            private EntityManager em;
            ...
        }

        So why can’t you just @Inject an entity manager? If you remember my first post about ambiguous injection, this is the same problem. You can have several persistence units (named with a string), so if you just use an @Inject, CDI will not know which persistence unit to inject. Instead you must produce the entity manager first, give it a name (if you don’t want the @Default) and then an @Inject as follow :

        1
        2
        3
        4
        5
        6
        7
        public class DatabaseProducer {
         
            @Produces
            @PersistenceContext(unitName = "cdiPU")
            @BookStoreDatabase
            private EntityManager em;
        }

        The DatabaseProducer class uses the @PersistenceContext to inject the entity manager with the persistence unit cdiPU. It gives it a name using a qualifier (@BookStoreDatabase) and produces it so it can now be injected in an EJB as follow :

        1
        2
        3
        4
        5
        6
        7
        8
        @Stateless
        public class ItemEJB {
         
            @Inject
            @BookStoreDatabase
            private EntityManager em;
            ...
        }

        Another nice use case is to produce JMS factories and destinations. @Resource allows you to get a JNDI reference to a specific JMS factory or destination, the qualifier @Order gives it a name, and the @Produces allows you to inject it :

        1
        2
        3
        4
        5
        6
        7
        8
        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 @Inject in a type-safe way :

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        @Stateless
        public class ItemEJB {
         
            @Inject @Order
            private QueueConnectionFactory orderConnectionFactory;
         
            @Inject @Order
            private Queue orderQueue;
            ...
        }

        Producing Java EE resources with producer methods

        The examples above are pretty simple : produce a field and you can then inject it. That’s called producer field. But sometimes you need a more complex business logic to produce a bean, that’s what we called producer method. Let’s take another use case. When you need to send a JMS message you always end up injecting a JMS Factory, a destination (queue or topic) creating a connection, then a session and so on until you actually send your message. Because this code is repetitive and error prone, why not externalize all of if into a single class and produce a session so the other components can use when sending a message. This class could look something like that :

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        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 the @Resource to get a reference of the QueueConnectionFactory and Queue. For the same reason I just explain above, you can have several JMS factories and destinations and you must distinguish them by their JNDI name. And because CDI does not allow you to give a string name on an injection point, you must still use @Resource instead of @Inject. The createOrderConnection method take a QueueConnectionFactory creates a QueueConnection and produces it (while giving it the name of @Order). If you look carefully at the method createOrderSession it takes the produced QueueConnection and creates a QueueSession. An external component then just needs to inject the JMS session without going through the process of creating it :

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        @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);
            }
            ...
        }

        Producing and… disposing

        I can hear you saying “ok, that’s nice, I have an external class doing all the plumbing and creating a connection and a session…. but who is going to close it ?“. Indeed, someone needs to free these resources by closing them. And that’s when CDI brings you another nice bit of magic : @Disposes.

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        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();
            }
        }

        To ask CDI to close a resource you just need to create a method which signature is the same as the one that created it (createOrderSession(@Order QueueConnection conn) creates a session and closeOrderSession(@Order QueueConnection conn) closes it) and add a @Disposes. CDI will dispose the resources in the right order (session first, connection second) for you. I haven’t mentioned it yet but CDI will create and dispose resources depending on the scope (resquest, session, application, conversation…). But that’s for another post.

        Conclusion


        As you’ve seen in my previous posts (Part IPart II), CDI is about injection (I’ll explain other topics later). So far I’ve been showing you how to inject beans, but you know now that you can inject anything (String, integer, entity manager, JMS factories…) anywhere (in POJOs, Servlets, EJBs…). You just need to produce what you need to inject (either with field producers or method producers).
        The next article will cover alternatives, so stay tuned.

        Download

        Download the code, give it a try, and give me some feedback.

        References

  • 相关阅读:
    js中null " "的区别
    简略的地图API应用
    JSON的一个例子(代码来源于网上)
    浅谈Event Flow、Process、Method及其Applications
    JS中object与constructor的分析
    浅谈for...in与for....of
    浅谈语言的过去与未来
    正则表达式的四个小应用
    BOM详细
    BOM
  • 原文地址:https://www.cnblogs.com/bigben0123/p/7109883.html
Copyright © 2011-2022 走看看