One way is to use Bordello to instantiate services in the application code. It is not dependency injection per se but it lets you test.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.concurrent.ConcurrentHashMap; | |
import java.util.concurrent.ConcurrentMap; | |
/** | |
* Service Locator Pattern - Dependency Injection by any other name. | |
* Allows unit tests to set a mock instance for some interface or class. | |
* | |
* <pre> SomeClass instance = Bordello.get(SomeClass.class); </pre> | |
* | |
* Requires simple constructor. Interfaces declare the implementing class. <pre> | |
* {@code @}Implementor(SomeTingImpl.class) | |
* public interface SomeTing { | |
* ... | |
* </pre> | |
*/ | |
public class Bordello { | |
// ConcurrentMap handles multi-threading and the Double-Check Locking problem. | |
private final static ConcurrentMap<Class<?>, Object> services = new ConcurrentHashMap<Class<?>, Object>(); | |
/** | |
* Acquire an implementation of a service. If one has not already | |
* been instantiated, instantiate the class defined by the | |
* Implementor annotation on the interface | |
*/ | |
public static <T> T get(Class<T> interfaceClass) { | |
Object service = services.get(interfaceClass); | |
if (service != null) { | |
return interfaceClass.cast(service); | |
} | |
// service does not yet exist | |
try { | |
Implementor annote = interfaceClass.getAnnotation(Implementor.class); | |
Class<?> implementingClass; | |
if (annote != null) { | |
implementingClass = annote.value(); | |
} else { | |
implementingClass = interfaceClass; | |
} | |
Object newservice = implementingClass.newInstance(); | |
service = services.putIfAbsent(interfaceClass, newservice); | |
if (service == null) { | |
// put succeeded, use new value | |
service = newservice; | |
} | |
} catch (Exception e) { | |
throw new RuntimeException(e); | |
} | |
return interfaceClass.cast(service); | |
} | |
/** | |
* Set an alternate service implementation. | |
* Typically only called in unit tests. | |
*/ | |
public static <T> void set(Class<T> interfaceClass, T providor) { | |
synchronized (interfaceClass) { | |
services.put(interfaceClass, providor); | |
} | |
} | |
/** | |
* Clear all previous instances - use before setting up for a unit test. | |
*/ | |
public static void reset() { | |
services.clear(); | |
} | |
} |
This is a variation on www.artima.com/weblogs/viewpost.jsp?thread=238562 that handles concurrency correctly.