Qualifiers

Qualifiers are required in some cases to resolve conflicts during dependency injection by identifying the correct bean with a qualified name. In the last section, we discussed injecting the dependencies. We injected defaultPreferredLanguage in the IdentityCreator class that was produced by the Producer function of the PreferredLanguageProducer class. If we want to inject another field of the same type, we then have a problem. If we have another function that produces a bean of the PreferredLanguage type, the CDI wouldn't know which one to consider and inject. In order to solve this, we can specify qualifiers, such as the @Named annotation.

@Named is a qualifier that is shipped by the CDI. It is annotated with @Qualifier and can qualify string values for instances to be injected.

Let's say that we want to have several preferredLanguage types, based on a context such as NativeEnglishSpeaker. In this case, we would need to specify @Named with the native-english-speaker string value. This is shown in the following code:

class IdentityCreator {
@Inject
@Named("native-english-speaker")
private lateinit var defaultPreferredLanguage: PreferredLanguage

fun createPerson(inputData: InputData): Person {
val person = Person()
person.preferredLanguage = if (inputData.preferredLanguage == null)
defaultPreferredLanguage
else
inputData.preferredLanguage
return person
}
}

In order to know which function or which producer is the correct one, we have to specify the Named annotation on the Producer function as well:

class PreferredLanguageProducer {
@Produces
@Named("native-english-speaker")
fun exposeDefaultPreferredLanguage(): PreferredLanguage {
return PreferredLanguage.EN_US
}
}

We can also specify another preferredLanguage to be injected in the IdentityCreator class that comes from another producer and has a different qualifier:

class PreferredLanguageProducer {
@Produces
@Named("hindi")
fun exposeDefaultPreferredLanguage(): PreferredLanguage {
return PreferredLanguage.HI
}
}

This can be injected as follows:

@Inject
@Named("hindi")
These qualifiers are not only annotated on producer functions; they can also be annotated directly on beans, as follows:

@Named("hindi")
class PreferredLanguageProducer {
  @Produces
  fun exposeDefaultPreferredLanguage(): PreferredLanguage {
    return PreferredLanguage.HI
  }
}

The Named qualifier with strings works fine, but this is not the best approach as it is not type safe. In this case, we are just relying on the string. If we make a typo, we don't get a warning until the application starts up. This means that we cannot just use the Named qualifier that comes with the string, but we can also use type-safe qualifiers if we define them in our application.

Let's say we want to have a @NativeEnglishSpeaker qualifier that itself is an annotation. We create a new annotation that we can use while creating Person:

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Documented
annotation class NativeEnglishSpeaker

These qualifiers are annotations that are annotated with @Qualifier from the CDI. An annotation in the Java EE world has to have a retention runtime policy so that it is visible at runtime.

Qualifiers need to be specified at the producer function. We can then inject PreferredLanguage in the IdentityCreator class in a type-safe way. Consider the following code:

class PreferredLanguageProducer {

@Produces
@NativeEnglishSpeaker
fun exposeDefaultPreferredLanguage(): PreferredLanguage {
return PreferredLanguage.EN_US
}
}

Here, we specified a producer function, exposeDefaultPreferredLanguage()which is annotated with @Produces and a qualifier annotation, @NativeEnglishSpeaker. Now, in the IdentityCreator class, we annotate the defaultPreferredLanguage field with the @NativeEnglishSpeaker and @Inject annotations so that a preferredLanguage created in the producer function gets injected in it:

class IdentityCreator {
@Inject
@NativeEnglishSpeaker
private lateinit var defaultPreferredLanguage: PreferredLanguage

fun createPerson(inputData: InputData): Person {
val person = Person()
person.preferredLanguage = if (inputData.preferredLanguage == null)
defaultPreferredLanguage
else
inputData.preferredLanguage
return person
}
}

We can now implement the custom qualifier types that can be used to specify at the injection point. We can use CDI to manage these beans.

We can now specify as many qualifiers as we want for all the custom qualifier types we are using if the types themselves are not sufficient to uniquely identify the injected instances. These qualifiers are handy in combination with the producers.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset