LookupAndSpring

From APIDesign

Jump to: navigation, search

An important subset of Spring is said to be an example of Dependency Injection. Lookup library, originally invented by NetBeans project seems to do similar kind of injection. Do these two APIs serve similar purpose or are they both quite different? Each of them is optimized for slightly different task. Could they strengthen themselves if they worked together? That it what we'll find soon out.

Imagine there is a GUI application to play the Anagrams Game. We have a base UI class called Anagrams that needs two additional interfaces to be injected. In Spring world, this can be done with a two arguments constructor:

Code from Anagrams.java:
See the whole file.

public class Anagrams extends javax.swing.JFrame implements UI {
    private WordLibrary wordLibrary;
    private Scrambler scrambler;
 
    public Anagrams(WordLibrary w, Scrambler s) {
        wordLibrary = w;
        scrambler = s;
        init();
    }
}
 

and an XML configuration file:

Code from Main.xml:
See the whole file.

<bean
    id="ui"
    class="org.apidesign.demo.anagramwithspringandlookup.Anagrams"
    autowire="autodetect"
/>
 

If used in classical Spring sense, the configuration file would have to refer to implementations of both constructor arguments, in order to let the Anagrams class to be properly wired. While this is fine in world of web applications, when one knows all the libraries running on the server, this may be too restricted in world of modular applications which are assembled together by someone else than the developer of the Anagrams implementation.

Java Extension Mechanism

To open door and allow others to do late injection to bind various implementations of WordLibrary and Scrambler classes, one can use the LookupAndSpring bridge. Then the actual implementations of needed interfaces are to be discovered using Java Extension Mechanism as abstracted by Lookup. The steps are simple. Get the LookupAndSpring and Lookup libraries and just compose your own XML context with the Java Extension one in your main method:

Code from Main.java:
See the whole file.

public static void main(String[] args) throws Exception {
    ApplicationContext servicesContext = SpringAndLookup.create(
        Lookup.getDefault(), "java.extensions"
    );
    ClassPathXmlApplicationContext mergedContext;
    mergedContext = new ClassPathXmlApplicationContext(
        new String[] { "Main.xml" },
        Main.class,
        servicesContext
    );
    UI ui = (UI)mergedContext.getBean("ui", UI.class);
    ui.display();
}
 

This creates an unfinished, open application to be extended by other JARs orchestrated together by those who assemble and deploy the final application. Yet, the coding style and testing of individual application's JavaBeans can be done using the classical Spring Dependency Injection style of development (just like the Anagrams class shows).

Double Injection

Yet, if there are extensions written according to the Java Extension mechanism or using the @ServiceProvider annotation offered by the Lookup library, one can have a JAR file containing result of compilation of:

Code from SimpleScrambler.java:
See the whole file.

@ServiceProvider(service=Scrambler.class)
public class SimpleScrambler implements Scrambler {
    private static final Random random = new Random();
 
    public String scramble(String word) {
}
}
 

and another one with another class:

Code from StaticWordLibrary.java:
See the whole file.

@ServiceProvider(service=WordLibrary.class)
public class StaticWordLibrary implements WordLibrary {
}
 

or even more JARs with different implementations of the same interfaces.

The composition of final application then becomes piece of cake. Instead of writing complicated binding XML file or doing inefficient on start scan of all resources in a package, one can just put selected JAR files into classpath and they look each other up effectively and unrestrainably.

In some sense this represent an example of double injection. First of all we instructed the Spring framework to do its Dependency Injection to provide necessary implementations to the Anagrams class constructor. Yet, these implementations are unknown at compile time of the JAR and they are not present in any single configuration file. Instead they are picked up from the runtime classpath by using Java Extension Mechanism as abstracted by Lookup.getDefault(). This is the second level of injection where the assembler injects various JARs into effective application classpath.

I am not sure if this kind of double injection can be useful in web applications where developer is also acting as assembler putting everything together, but in modular applications it has its place. If you are interested, see and use the Spring/Lookup bridge:

Leave here comments about your satisfaction, disappointment and my confusion.

<comments/>

Personal tools