ServiceLoader or "Sometimes you just don't need a full blown dependency injection container"
I like Springframework very much and use it in my day to day business as it eases development in many places. A few days ago I started using Hibernate Envers in order to implement a simple auditing solution for a JPA based application. I was faced with the (typical) requirement that the username of the user currently logged in was written into the audit entity. With Envers you would do that using a org.hibernate.envers.RevisionListener. This is instantiated by the Envers runtime. That listener required access to the FacesContext (it’s a JSF based application) to get the user principal from the HTTP request. Not a big deal in general! Still I wanted to keep that testable and usable in various environments, so an abstraction was needed and finally something would have to inject that into my listener.
As I said I’m a fan of Spring and this would be the perfect job and eventually one would propose “yeah, let’s do Spring AOP with AspectJ weaving”. As this application was already in production I didn’t want to add that at this time and was seeking for something more simple but still elegant.
Java as of 1.6 has the concept of SPI and ServiceLoader already built-in. I had never used it before. Here it seemed to perfectly match and it did.
import java.security.Principal;
public interface UserPrincipalProvider {
Principal getUserPrincipal();
}
import java.security.Principal;
public class TestUserPrincipalProvider implements UserPrincipalProvider {
public Principal getUserPrincipal() {
return new Principal() {
public String getName() {
return "TEST";
}
};
}
}
A file named META-INF/services/at.package.auditing.UserPrincipalProvider would be responsible for registering the provider implementation, it only contains one or multiple class names:
at.package.auditing.TestUserPrincipalProvider
For getting a runtime instance of such a provider I implemented a helper like
public static <T> T lookup(Class<T> clazz) {
Iterator<T> iterator = ServiceLoader.load(clazz).iterator();
return iterator.hasNext() ? iterator.next() : null;
}
It is called like ...lookup(UserPrincipalProvider.class).
