This article describes how Google Guice's Injector and its Injector Holder are used in OpenAM. The relevant code is in a package called Guice Core from ForgeRock. The source code is ** here ** Can be referenced. Let's start with a basic description of the Guice Injector.
It's almost Christmas / year-end, so I'll explain using two classes, ChristmasGreeting
and NewYearGreeting
, which implement the SeasonsGreeting
interface.
What you should pay attention to is the part where the Guice # createInjector (Module ...)
method gets an instance of ʻInjector`.
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
public class Example01 {
public interface SeasonsGreeting {
void greeting();
}
public static class ChristmasGreeting implements SeasonsGreeting {
@Override
public void greeting() {
System.out.println("Merry Christmas!");
}
}
public static class NewYearGreeting implements SeasonsGreeting {
@Override
public void greeting() {
System.out.println("Happy New Year!");
}
}
public static void main(String[] args) {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override protected void configure() {
bind(SeasonsGreeting.class).to(ChristmasGreeting.class);
}
});
SeasonsGreeting sg = injector.getInstance(SeasonsGreeting.class);
sg.greeting();
}
}
The configure ()
method connects the SeasonsGreeting
interface with its implementation, ChristmasGreeting
. The ʻinjector generated accordingly will now return an instance of
ChristmasGreeting`.
Execution result
Merry Christmas!
In the above example, Guice # createInjector (Module ...)
had only one argument, but you can specify multiple arguments as shown below.
public static Injector createInjector(java.lang.Iterable<? extends Module> modules)
If you use this, you can read multiple settings specified by the class that extends Module
at once. The InjectorHolder, described below, provides a fixed "place" for repeated use of the ʻInjector` instance thus generated.
From the name "Holder" of InjectorHolder, it feels like saving the created instance of ʻInjector and taking it out and using it as needed. However, in reality, ʻInjector
is generated inside ʻInjectorHolder` and used internally. It may be closer to the reality if you call it a rapper because you will not be taken out.
In the code below, the enum will come out from the beginning. How to implement a singleton using an enum with only one element (ʻINSTANCE) Introduced in item 3 of [Effective Java 3rd Edition](https://www.amazon.co.jp/Effective-Java-%E7%AC%AC3%E7%89%88-Joshua-Bloch/dp/4621303252) It has been. Here we use it to define ʻInjectorHolder
as a singleton.
Notice the part of the private constructor that uses ʻInjectorFactory to generate ʻInjector
.
public enum InjectorHolder {
/**
* The Singleton instance of the InjectorHolder.
*/
INSTANCE;
private Injector injector;
/**
* Constructs an instance of the InjectorHolder and initialises the Guice Injector.
*/
private InjectorHolder() {
InjectorFactory injectorFactory = new InjectorFactory(new GuiceModuleCreator(),
new GuiceInjectorCreator(), InjectorConfiguration.getGuiceModuleLoader());
try {
injector = injectorFactory.createInjector(InjectorConfiguration.getModuleAnnotation());
} catch (Exception e) {
e.printStackTrace();
throw new IllegalStateException(e);
}
}
/**
* @param clazz The class to get an instance of.
* @param <T> The type of class to get.
* @return A non-null instance of the class.
*/
public static <T> T getInstance(Class<T> clazz) {
return INSTANCE.injector.getInstance(clazz);
}
The final getInstance (Class <T> clazz)
method now returns an instance of the clazz
created using it instead of returning the singleton itself or the ʻInjector` instance registered with the singleton. I have.
Now let's see how ʻInjectorFactory instantiates ʻInjector
.
As mentioned earlier, when generating ʻInjector, you can specify multiple instances of a class that extends
Module. Each extension class overrides the
Configure ()method with different settings. Annotation
@GuiceModule` is used to determine which extension class to load. Specifically, the Injector Factory
Module
annotated with @GuiceModule
.Guice # createInjector ()
to generate ʻInjector`We are doing the processing. The code below is the introductory part. Other parts are omitted because they are long. For details, see [Public Code](https://github.com/openam-jp/forgerock-guice/blob/master/forgerock-guice-core/src/main/java/org/forgerock/guice/core/ See InjectorFactory.java).
final class InjectorFactory {
/**
* Creates a new Guice injector which is configured by all modules found by the {@link GuiceModuleLoader}
* implementation.
*
* @param moduleAnnotation The module annotation.
* @return A non-null Guice injector.
*/
Injector createInjector(Class<? extends Annotation> moduleAnnotation) {
/*
This does not need to by synchronized as it is only ever called from the constructor of the
InjectorHolder enum, which is thread-safe so no two threads can create an injector at the same time.
This does mean that this method MUST not be called/used by another other class!
*/
return injectorCreator.createInjector(createModules(moduleAnnotation));
}
We have seen how the Injector Holder works. In the following, we will look at how it is used in OpenAM.
The work of making OpenAM compatible with Google Guice was done long ago. Generally speaking, getting large software like OpenAM compatible with Google Guice is not easy. You'll start with a simple subtree that's closer to the branches and leaves of the dependency tree, and follow the tree towards more complex objects. In a complicated tree, it may not be possible to rewrite at once. In such a case, place the InjectorHolder at the "border" between the rewritten class and the unrewritten class. Create an object of the class that has been rewritten using InjectorHolder in the incomplete class. The way to resolve the dependency after that is to leave it to Google Guice. As the rewriting progresses, the Injector Holder will also move. By doing this, even complicated software can be gradually rewritten.
In the first place, it may not be possible to support Google Guice, or there is no merit to support it. In the case of OpenAM, this is the authentication service. OpenAM has a mechanism to manage various authentication module instances independently for multi-factor authentication. Since it is difficult to make this compatible with Google Guice, Injector Holder uses it at the level of individual authentication modules.
At OpenAM, we have seen that the Injector Holder is placed at the "border" between "non-Guice World" and "Guice World". If you are interested, please see the Source Code published in the OpenAM Consortium.
Recommended Posts