Once you get in the habit of using dependency injection in your software, there is no turning back. That is, unless you are working on Java ME. In fact, it is even impossible to use any of the popular dependency injection mechanisms at all.
The reason why all of the popular dependency injection framework fail on ME, is because they all rely heavily on reflection. For instance, in order to set fields of an object to a certain value. Or to determine the type of the object injected, and do some adaptation on the fly. And, unfortunately, Java ME does not support reflection.
The attempts to create a Java ME-based dependency injection framework that I know of (like the Israfil micro container) all try to compensate for not having reflection by putting the burden of configuration on the developer. As a consequence, you end up writing more code than what you are used to; not necessarily desirable for a platform on which every byte counts.
So I decided to take another stab at it. I decided to implement reflection on Java ME. Just kidding.
What I did instead is, first of all, decide that I do not actually need the just-in-time runtime decisions on how my objects are going to be wired. I decided that it would be fine, if all of that would be done at build time. At build time I have all the tools to obtain the metadata I need. At runtime, I hardly have any at all.
Next I figured that - if I want this dependency injection mechanism to survive - I better stick to what people are used to. So instead of defining yet another way of expressing dependencies, I figured it would be good to build on the existing popular frameworks. Spring in particular.
After that, I decided that I did not need all features of Spring. Not even all of the features provided by its core dependency injection mechanism. So I decided to have my own meta model of the instances and their dependencies, and have the ability to instantiate that model from a Spring application context.
And then the fundamental trick: instead of using this meta model to construct a BeanFactory at runtime, this new dependency injection mechanism uses the meta model to construct a BeanFactory at build time. It will source code for the entire BeanFactory. It turns out, this is possible, and it also turns out it leaves you with no runtime dependencies at all, which is also a nice side effect.
So, here is an example of a Spring application context:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean name="teacher1" class="com.tomtom.di.spring.Teacher">
<property name="name" value="Wilfred Springer"/>
<property name="age" value="35"/>
<property name="courses">
<list>
<ref bean="course1"/>
<ref bean="course2"/>
<bean class="com.tomtom.di.spring.Course">
<property name="topic" value="C++"/>
</bean>
</list>
</property>
</bean>
<bean name="course1" class="com.tomtom.di.spring.Course">
<property name="topic" value="Java"/>
</bean>
<bean name="course2" class="com.tomtom.di.spring.Course">
<property name="topic" value="Erlang"/>
</bean>
</beans>
… and here is the BeanFactory that is created from it:
package com.tomtom.spring;
/**
* An object factory, providing access to a network of wired objects,
* some of them lazily instantiated. You can refer to some objects by
* name. The named objects managed by this class are:
*
* <ul>
* <li>teacher1 : {@link com.tomtom.di.spring.Teacher}</li>
* <li>course1 : {@link com.tomtom.di.spring.Course}</li>
* <li>course2 : {@link com.tomtom.di.spring.Course}</li>
* </ul>
*
*/
public class BeanFactory {
/**
* A cached reference to the bean named "teacher1".
*/
private com.tomtom.di.spring.Teacher teacher1;
/**
* A cached reference to the bean named "course1".
*/
private com.tomtom.di.spring.Course course1;
/**
* A cached reference to the bean named "course2".
*/
private com.tomtom.di.spring.Course course2;
/**
* Returns an object by looking it up by its name.
*
* @return The object with the given name, constructed by the BeanFactory.
* @see BeanFactory
*/
public Object getBean(String name) {
if ("teacher1".equals(name)) return getTeacher1();
if ("course1".equals(name)) return getCourse1();
if ("course2".equals(name)) return getCourse2();
return null;
}
/**
* Returns the bean named "teacher1".
*
* @return The bean named "teacher1".
* @see #createSource0()
*/
final private com.tomtom.di.spring.Teacher getTeacher1() {
if (teacher1 == null) {
teacher1 = createSource0();
}
return teacher1;
}
/**
* Returns the bean named "course1".
*
* @return The bean named "course1".
* @see #createSource9()
*/
final private com.tomtom.di.spring.Course getCourse1() {
if (course1 == null) {
course1 = createSource9();
}
return course1;
}
/**
* Returns the bean named "course2".
*
* @return The bean named "course2".
* @see #createSource11()
*/
final private com.tomtom.di.spring.Course getCourse2() {
if (course2 == null) {
course2 = createSource11();
}
return course2;
}
/**
* Constructs one of the required instances of {@link com.tomtom.di.spring.Teacher}.
*
* @return One of the required instances of {@link com.tomtom.di.spring.Teacher}.
*/
final private com.tomtom.di.spring.Teacher createSource0() {
com.tomtom.di.spring.Teacher result = new com.tomtom.di.spring.Teacher();
result.setCourses(createSource8());
result.setName("Wilfred Springer");
result.setAge(35);
return result;
}
/**
* Constructs one of the required instances of {@link com.tomtom.di.spring.Course}.
*
* @return One of the required instances of {@link com.tomtom.di.spring.Course}.
*/
final private com.tomtom.di.spring.Course createSource9() {
com.tomtom.di.spring.Course result = new com.tomtom.di.spring.Course();
result.setTopic("Java");
return result;
}
/**
* Constructs one of the required instances of {@link com.tomtom.di.spring.Course}.
*
* @return One of the required instances of {@link com.tomtom.di.spring.Course}.
*/
final private com.tomtom.di.spring.Course createSource11() {
com.tomtom.di.spring.Course result = new com.tomtom.di.spring.Course();
result.setTopic("Erlang");
return result;
}
/**
* Constructs one of the required instances of {@link com.tomtom.di.spring.Course}.
*
* @return One of the required instances of {@link com.tomtom.di.spring.Course}.
*/
final private com.tomtom.di.spring.Course createSource7() {
com.tomtom.di.spring.Course result = new com.tomtom.di.spring.Course();
result.setTopic("C++");
return result;
}
/**
* Constructs a java.util.Vector of objects, to be injected elsewhere.
*
* @return A list of objects.
*/
final private java.util.Vector createSource8() {
java.util.Vector result = new java.util.Vector();
result.add(getCourse1());
result.add(getCourse2());
result.add(createSource7());
return result;
}
}
Now, the only public operation on this class is obviously the getBean operation. It’s all you need. You could try to understand all of the other operations, but this is the code that you should not be worrying about. It’s just provided here for illustrative purposes.
The current state of the framework is that there is Maven plugin that is doing all of this. There are a couple of things that I want to add, such as completing constructor injection and support for factory-method and a factory-bean. But the current incarnation is already working fine.
Now, if you have made it this far, you hopefully started to search for the download button. Now, unfortunately, this stuff is not open sourced yet. However, if you feel this is what you need, I suggest you make yourself heard, by posting your wish for getting your hands on this code as a comment on this blog. The more people displaying their interest in getting this out there as open source, the easier it will be for me to convince some people to do it.
Technorati Tags: java, dependency injection, spring