SyntheticGetterClassLoader

A ClassLoader that synthesizes getters on load-time to use locator objects.

Download sources for version 2.0.

Download sources for version 1.0 that modified the class in-place.


Dependency Injection is a methodology whereby prepping service objects for use in an application is done declaratively, usually using an XML file. Values for properties and references to collaborator objects are configured by the application assembler, and are set up by the framework when the object is initialized.

This is to be contrasted with the "traditional" configuration method of making each object responsible to reach out and retrieve values and objects from property and object repositories (called "Service Locators"). A very nice exposition by Martin Fowler can be found in an article of his, "Inversion of Control Containers and the Dependency Injection pattern". In addition to Mr Fowler's point, I want to add that dependency injection allows fexibility in what service objects are supplied to which requester. Using a Service Locator means that the whole system will only use only one of each kind of service object, since all code will ask and get the same "TransactionManager", "AuthenticationAgent" etc.

Inversion of Control (IoC) containers provide an elegant framework that helps so long as the collaborator objects are singleton service objects, that are provided to the object after initialization. Unfortunately, using non-singleton objects of which we need fresh copies each time, means we need to use the container's API to retrieve it and we introduce back the dependency we strived to eliminate.

Spring solves this dillema by using "method injection", whereby the lookup method is delegated to another object through AOP. This other object does use the container's API explicitly, which means that the dependency problem is shifted to another place but not eliminated. In addition, this is a powerful solution for a problem that, I believe, in 99% of the cases can be solved more simply.

Using the SyntheticGetterClassloader, one can write code that relies on abstract getter methods to return appropriate objects, expecting the ClassLoader to provide synthetic bodies that use the IoC container's API to retrieve the objects. This eliminates the dependency in the source code. It is not far-fetched to imagine that the case below (in Spring syntax), where "Foo" has an abstract "getBar()" method returning a "Bar", can be recognized and dealt with automatically by the container - without even special syntax.

<bean singleton="false" id="Bar" class="Bar"/> <bean id="Foo" class="Foo"> <property name="bar"> <bean ref="Bar"/> </property> </bean>

This first version of the ClassLoader modified the class to instrument in-place, which means that one could use it through any of its superclasses or interfaces, but not directly. This is not a bad thing in principle, since systems should not be designed to rely on concrete classes, anyway, but some times you need the extra flexibility.

The current version subclasses the class so that this restriction does not exist. The way to use the ClassLoader is now to load className+SyntheticGetterClassLoader.implSuffix which is the name of the generated subclass.

The ClassLoader is initialized with a target class name and a map from property names to object ids. The target class is expected to be an abstract class with abstract getters for all properties that are keys in the map. The loaded class will have synthetic bodies for all those getters, that will use an "ObjectLocator" object to retrieve an object with the corresponding id. The "ObjectLocator" object is used as a prototypical stand-in for an IoC container, implementing the "getObject" method. The "ObjectLocator" field is added to the class and its value needs to be supplied to the object instantiated from the class, at run-time using reflection.

Classes "Foo" and "Bar" are supplied as test classes.

package name.dsouflis.aop.synthgetter; public class Bar { String name; public Bar(String name) { this.name = name; } public String toString() { return new StringBuilder().append("Bar(").append(name).append(")").toString(); } } package name.dsouflis.aop.synthgetter; public abstract class Foo { public abstract Bar getBar(); public String toString() { return new StringBuilder().append("Foo(").append(getBar().toString()).append(")").toString(); } }

As you can see, class "Foo" has an abstract "getBar()" method which returns a "Bar" object. Class "Test" has a "main" method that creates a "Foo" instance, injects an "ObjectLocator" using reflection and calls its "toString()" method. This demonstrates that "getBar()" actually returns the object supplied by the "ObjectLocator".