Securing Your Software Supply Chain with JFrog and Azure
Register Today

Injection with CDI (Part I)

· Interview
Save
7.00K Views

download 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  two articles  , 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_23
glassfish 3.1
maven 3.0.2

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 :

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

@stateless
public class itemejb {

    @persistencecontext(unitname = "cdipu")
    private entitymanager em;
    ...
}

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 :


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

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

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

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

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

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

@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  alternatives and producers  , so stay tuned.

download

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

references

Opinions expressed by DZone contributors are their own.


Comments
X