Burak Aktas Software Engineer

Create Qualifiers for CDI Beans

As I described in my previous post, we can define and inject cdi beans by @Named annotation. Well according to the CDI specification (JSR-299) injecting beans by their names is legacy and tend to cause issues (if a bean is tried to be injected by an undefined/wrong name then we will get errors which are hard to find). Thus, we are going to create different annotations (qualifiers) to inject different type of beans from same interface. Moreover, we will use Weld CDI implementation.

Create a Qualifier

Creating your own qualifier is easy. We have to create a standard annotation and mark it with @Qualifier.

import javax.inject.Qualifier;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({FIELD, METHOD, TYPE, PARAMETER})
public @interface <annotation_name> {
}

So, according to the qualifier template we will need three annotations; Bmw, Honda, and Ford.

Bmw.java

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({FIELD, METHOD, TYPE, PARAMETER})
public @interface Bmw {
}

Honda.java

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({FIELD, METHOD, TYPE, PARAMETER})
public @interface Honda {
}

Ford.java

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({FIELD, METHOD, TYPE, PARAMETER})
public @interface Ford {
}

Notes

  • Retention determines at what point an annotation is discarded. More explanation can be seen from here.
  • Target defines the type of our annotation. Explanation with clear examples can be seen from here.

I will again use the same code examples which I used in my previous posts. There is one interface called AutoService and three implementations of it.

AutoService.java

public interface AutoService {
    String getService();
}

BMWAutoService.java

@Bmw
public class BMWAutoService implements AutoService{

    @Override
    public String getService() {
        return "You chose BMW auto service";
    }
}

FordAutoService.java

@Ford
public class FordAutoService implements AutoService{

    @Override
    public String getService() {
        return "You chose Ford auto service";
    }
}

HondaAutoService.java

@Honda
public class HondaAutoService implements AutoService{

    @Override
    public String getService() {
        return "You chose Honda auto service";
    }
}

Now we are completely ready to inject our cdi beans by their qualifiers! So, lets see three different injection examples – Field, Constructor and Setter method injection -.

1. Injection Through Fields

public class Example {
 
    @Inject
    @Bmw
    private AutoService bmwAutoService;
 
    @Inject
    @Honda
    private AutoService hondaAutoService;
 
    @Inject
    @Ford
    private AutoService fordAutoService;
 
    ...
}

2.Injection Through Setter Methods

public class Example {
 
    private AutoService bmwAutoService;
    private AutoService hondaAutoService;
    private AutoService fordAutoService;
 
    @Inject
    public void setBmwAutoService(@Bmw AutoService bmwAutoService) {
        this.bmwAutoService = bmwAutoService;
    }
 
    @Inject
    public void setHondaAutoService(@Honda AutoService hondaAutoService) {
        this.hondaAutoService = hondaAutoService;
    }
 
    @Inject
    public void setFordAutoService(@Ford AutoService fordAutoService) {
        this.fordAutoService = fordAutoService;
    }
   
   ...
}

3.Injection Through Constructor

public class AutoServiceCallerImp 
 
    private AutoService bmwAutoService;
    private AutoService hondaAutoService;
    private AutoService fordAutoService;
 
    @Inject
    public AutoServiceCallerImp(@Bmw AutoService bmwAutoService,
                             @Honda AutoService hondaAutoService,
                             @Ford AutoService fordAutoService) {
 
        this.bmwAutoService = bmwAutoService;
        this.fordAutoService = fordAutoService;
        this.hondaAutoService = hondaAutoService;
    }

    ...
}

There is also a second way to use qualifiers to prevent from annotation overflow. In normal life we have tens of auto models. So, instead of creating lots of qualifiers, we can only create one qualifier and enum to define the type of annotation we want to use -enum is optional we can specify types by integer, string etc-. With this way we will be able to handle lots of annotations easily. Let us see;

AutoType.java

public enum AutoType {
    Bmw, Ford, Honda
}

Auto.java

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({METHOD, TYPE, PARAMETER, FIELD})
public @interface Auto {
    AutoType type() default AutoType.Bmw;
}

default property is optional here. It means if we dont specify a type then the default value will be used. Now let's an example by field injection.

public class AutoServiceCallerImp implements AutoServiceCaller{

    @Inject
    @Auto(type = AutoType.Bmw)
    private AutoService bmwAutoService;

    @Inject
    @Auto(type = AutoType.Honda)
    private AutoService hondaAutoService;

    @Inject
    @Auto(type = AutoType.Ford)
    private AutoService fordAutoService;

    ...
}

So, as we can see we define the type of our cdi beans just by giving type value, and we have only used @Auto annotation.