TOTD #161: Java EE 6 CDI Qualifiers explained - @Default, @Any, @New, @Named

The CDI specification (JSR-299) defines "Qualifer" as a means to uniquely identify one of the multiple implementations of the bean type to be injected. The spec defines certain built-in qualifiers (@Default, @Any, @Named, @New) and new qualifiers can be easily defined as well. This Tip Of The Day (TOTD) discusses the in-built qualifiers and how they can be used.

The @Named qualifier makes a bean EL-injectable, that's it!

The @Default qualifier, appropriately called as "default qualifier", exists on all beans without an explicit qualifer, except @Named. Consider the following bean type and implementation:

public interface Greeting {
    public String greet(String name);
}


public class SimpleGreeting implements Greeting {
    public String greet(String name) {
        return "Hello " + name;
    }
}

So SimpleGreeting defined above is equivalent to:

@Default
public class SimpleGreeting implements Greeting {
    . . .
}

Similarly

@Named
public class SimpleGreeting implements Greeting {
    . . .
}

is equivalent to:

@Named
@Default
public class SimpleGreeting implements Greeting {
    . . .
}

The default qualifier works for the type injection as well. So

@Inject Greeting greeting;

and

@Inject @Default Greeting greeting;

are equivalent. However it is not recommended to use @Named as injection point qualifier, except in the case of integration with legacy code that uses string-based names to identify beans.

@Any is another in-built qualifier on all beans, including the ones with implicit or explicit @Default qualifier, except @New qualified beans (more on this later). So the SimpleGreeting implementation is equivalent to:

@Default @Any
public class SimpleGreeting implements Greeting {
    . . .
}

And can be injected as:

@Inject @Any Greeting greeting;

or even

@Inject @Any @Default Greeting greeting;

Now lets add a new implementation of the Greeting as:

public class FancyGreeting implements Greeting {
    public String greet(String name) {
        return "Nice to meet you, hello " + name;
    }
}

Now all of the following injections fail:

@Inject Greeting greeting;
@Inject @Default Greeting greeting;
@Inject @Any Greeting greeting;
@Inject @Default @Any Greeting greeting;

with the "ambiguous dependencies" error because both the implementations now have @Default and @Any qualifiers.

This can be resolved by adding a new qualifier called @Fancy to the FancyGreeting implementation as:

@Fancy
public class FancyGreeting implements Greeting {
    . . .
}

which is also equivalent to:

@Fancy @Any
public class FancyGreeting implements Greeting {
    . . .
}

Now all the following inject statements:

@Inject Greeting greeting;
@Inject @Default Greeting greeting;
@Inject @Any @Default Greeting greeting;

will inject the SimpleGreeting implementation. However

@Inject @Any Greeting greeting;

will throw an "ambiguous dependency" error because now there are two implementations with that qualifier. The right implementation can be picked by specifying the additional qualifier such as:

@Inject @Any @Default Greeting greeting;

will inject the SimpleGreeting implementation and

@Inject @Any @Fancy Greeting greeting;

will inject the FancyGreeting implementation. The following injection will work anyway:

@Inject @Fancy Greeting greeting;

Lastly

@Inject @Default @Fancy Greeting greeting;

will give an "unsatisfied dependency" error because any bean with an explicit qualifier, except @Named, does not have the @Default qualifier.

@Any may also be used to add qualifiers programmatically such as:

@Inject @Any Instance<Greeting> greeting;
greeting.select(new AnnotationLiteral<Fancy>(){}).get();

will return a Greeting implementation with @Fancy qualifier. Here the "javax.enterprise.inject.Instance" and "javax.enteprise.util.AnnotationLiteral" classes are defined by the CDI spec.

@Any may also be used to iterate over all Greeting implementations such as:

@Inject @Any Instance<Greeting> greeting;
for (Greeting g : greeting) {
    // do something with g
}

The @New qualifier allows to obtain a depdendent object of a specified class, independent of the declared scope. So if SimpleGreeting is defined as:

@RequestScoped
public class SimpleGreeting implements Greeting {
    . . .
}

and injected as:

@Inject Greeting greeting;

then a request-scoped Greeting implementation (SimpleGreeting in this case) is injected. However if it is injected as:

@Inject @New Greeting greeting;

then the injected SimpleGreeting is in the @Dependent scope and only has @New qualifier (neither @Default or @Any).

I tried all of this using GlassFish 3.1 and NetBeans 7.0 that provides complete development and fully-clustered deployment environment for your Java EE 6 applications. Where are you deploying your Java EE 6 apps ?

Technorati: totd cdi qualifier javaee6 glassfish

Comments:

Have you purposefully missed showing how to create a new Qualifier for @Fancy or is it not needed?

Posted by Sreekanth on April 24, 2011 at 10:30 PM PDT #

Sreekanth,

That is pretty straight forward and boiler-plate code so left it out. Moreover NetBeans let you generate it with a single click anyway :-)

Posted by Arun Gupta on April 24, 2011 at 11:07 PM PDT #

Just to make it complete for a newbie :-)

Posted by Sreekanth on April 24, 2011 at 11:13 PM PDT #

There are a couple of requirements on qualifier annotations that make me think "boilerplate" is not a fair description. Most notably, the qualifier annotations need to be annotated with @Qualifier and have runtime retention. So I think a small example would add quality to an otherwise very helpful article.

Posted by guest on May 30, 2011 at 06:00 PM PDT #

Thanks for the post. It covers exactly what I was looking for. However, I'm not getting the expected results when using @Inject @Any with Instance.

When I try to access the Instance, it is always null. Any suggestions? Does it matter if the other implementations are in a separate Java EE Module? I'm using Glassfish 3.1 and NetBeans 7.0.1 JDK 1.6.0_27

e.g.
Module A

@Stateless
@LocalBean
@Interceptors({LogInterceptor.class})
@TransactionAttribute(TransactionAttributeType.MANDATORY)
public class GalleryService {
@EJB private CRUDService crudService;
@Inject @Any private Instance<OperationService> providers;
...

public void invoke(String galleryId, OperationDescriptor descriptor) {
for (OperationService provider : providers) {
...
}
}
}

Module B

@Named
@Singleton
@Startup
@Local(OperationService.class)
@Interceptors({LogInterceptor.class})
@TransactionAttribute(TransactionAttributeType.SUPPORTS)
public class BacktestService extends ABOperationService<BacktestDescriptor> {
...
}

Posted by guest on May 21, 2012 at 12:21 AM PDT #

Should you be injecting ABOperationService instead ? You can try asking at http://seamframework.org/Community/WeldUsersForum.

CDI recommends specifying interceptors in "beans.xml" as that is more loosely coupled so you may like to fix that as well.

Posted by Arun Gupta on May 21, 2012 at 09:45 AM PDT #

The issue was a missing beans.xml, thus CDI was not working in that module. Now that I've got my beans.xml (Doh!), I'll look into moving the interceptor there. Thanks for the tip.

Again, great post. There are not a lot of places where all the combinations are pulled together into a single explanation like this.

Posted by Mike on May 21, 2012 at 03:04 PM PDT #

Arun,

"The @Default qualifier... exists on all beans without an explicit qualifer, except @Named."

"Similarly
@Named public class SimpleGreeting implements Greeting {     . . . }
is equivalent to:
@Named @Default public class SimpleGreeting implements Greeting..."

Isn't this a contradiction?

Posted by guest on December 29, 2012 at 03:26 AM PST #

Post a Comment:
Comments are closed for this entry.
About

profile image
Arun Gupta is a technology enthusiast, a passionate runner, author, and a community guy who works for Oracle Corp.


Java EE 7 Samples

Stay Connected

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
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
   
       
Today