To implement simple setter dependency injection with annotations, you can use reflection
to find all setters and lookup the corresponding singletons that should be injected as
parameters.
Example use to inject dependencies:
ApplicationContext appContext = new ApplicationContext();
// use ServiceImpl as implementation of Service
appContext.bind(Service.class, ServiceImpl.class);
Activity1 activity = new Activity1();
// inject dependencies into activity: use setService to set ServiceImpl singleton
appContext.inject(activity);
String value = activity.execute();
Lookup all methods and invoker setters:
public void inject(Object target) {
Class targetClass = target.getClass();
Method[] methods = targetClass.getMethods();
for (Method method : methods) {
if (method.getName().startsWith("set")) {
// method is a setter, check if method has "@Autowired" annotation
Autowired inject = method.getAnnotation(Autowired.class);
if (inject != null) {
// method has annotation
findParametersAndInvoke(target, method);
}
}
}
}
Try to find parameters in the registered singleton map and invoke the setter:
private void findParametersAndInvoke(Object target, Method method) {
Class[] parameterTypes = method.getParameterTypes();
Object[] parameters = new Object[parameterTypes.length];
int parameterIndex = 0;
boolean allParametersInstantiated = true;
for (Class parameterType : parameterTypes) {
if (singletons.containsKey(parameterType)) {
// singleton for parameter type is registered
parameters[parameterIndex] = singletons.get(parameterType);
} else if (binding.containsKey(parameterType)) {
// type is registered and not instantiated yet
parameters[parameterIndex] = createSingleton(parameterType);
} else {
allParametersInstantiated = false;
}
parameterIndex++;
}
if (allParametersInstantiated) {
try {
method.invoke(target, parameters);
} catch (IllegalArgumentException e) {
throw new InjectException(e);
} catch (IllegalAccessException e) {
throw new InjectException(e);
} catch (InvocationTargetException e) {
throw new InjectException(e);
}
}
}
You might be thinking "Why make your own application context while we have Spring, Guice
and many other IoC containers?". The reason is Android: I would like to use something like
Spring or Guice in my Android application. Although these containers are considered
"lightweight" (compared to EJB, I guess), they are still a bit too heavy to use on a phone.
The size of jars and the fact that your Android application might be stopped and started
frequently does not work well with the existing containers.
That, and also the learning experience makes me experimenting with this stuff. So far,
working with annotations makes this very easy to implement and seems efficient enough.
Implementation: setter-annocation-injection.zip












