Injection with CDI (Part II)

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

@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 :

@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) :

@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 :

@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) :

@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 :

@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 :

@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

Comments
7 Responses to “Injection with CDI (Part II)”
  1. Vasyl Keretsman says:

    Hi Antonio,

    the CDI articles are very useful.

    I was wondering if you could provide a CDI example for the following case.

    Let’s say we have the following service

    class MyService {
    private Strategy strategy
    }

    class StrategyA implements Strategy{
    }

    class StrategyB implements Strategy{
    }

    My application needs two instances of MyService with different “strategy” instances injected.

    Is it possible to solve this with annotations?

    P.S. I only came up with Spring-based solution using XML or JavaConf . But I am wondering whether Annotations can solve it.

    Thanks

    Regards,

    Vasyl

  2. jose luis says:

    Hi vasyl !

    I think i can solve your problem :

    You should do this :

    @Inject private Strategy strategy;

    Then, in each subclass, you should declare one as @Default, and the other as @alternative

    Finally, in the beans.xml , you declare the alternative class

    So :, if using StrategyA as default, you need not do to anything.
    Otherwise, if using B, you should have this in beans.xml

    com.yourcompany.StrategyB

    Hope this helps.

  3. Pedro Kowalski says:

    Another solution for your problem, Vasyl, could be a definition of separate annotations/qualifiers, i.e. @StrategyA and @StrategyB and use them as follows:

    @Inject @StrategyA private Strategy strategy;
    @Inject @StrategyB private Strategy strategy;

    The path you choose depends on what you want to achieve. Solution posted by Jose is ideal for distinction between development and production environment (you can substitute the beans.xml file which turns on/off alternatives) and the second solution could be fine if you want to use both of them at the same time (despite development or production env).

    Cheers!

  4. Pedro Kowalski says:

    Oh and one more thing.

    Antonio, you did use @Inject on your EJB instead of using EJB’s container @EJB annotation.

    Is it possible and is it working fine? I thought that you need to have a producer defined in the code which acts as a middle layer between EJB container and CDI. I mean, I thought that you MUST do something like:

    @Produces @EJB ItemEJB myEJB;

    and then you can use:

    @Inject ItemEJB itemEJB

    So if I want to use @Inject with my EJB do I HAVE TO declare this @Produces annotation or does it depend on the EJB Container implementor?
    Or maybe you did provide a @Producer of your EJB but you just didn’t show it in the article in sake of brevity?

    Thanks in advance!

    Cheers!

    • agoncal says:

      Yes. You can “replace all” @EJB with @Inject and it will work fine, no need to have producers in this case

      • Pedro Kowalski says:

        Antonio, thanks for fast response!

        So the EJB + CDI container is *required* to let user to use @Inject instead of @EJB? Is this a part of the spec and can I rely on it?

        Than I need a producer only in case I want to i.e. explicitly bind my EJB with some JNDI link? In other words (example taken from http://docs.jboss.org/weld/reference/1.0.0/en-US/html/resources.html) for:

        @EJB(ejbLink=”../their.jar#PaymentService”)
        PaymentService myEjb;

        I need to define a producer to use — @Inject PaymentService injEjb — , but for:

        @EJB PaymentService myEjb;
        I do not need one?

        Cheers!

        PS. OT: Great talk about CDI and JEE 6 at GeeCon ’11 – I hope you’ll visit us next year :-)

Trackbacks
Check out what others are saying...


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 8,088 other followers

%d bloggers like this: