Implement Service Locator in Java only when central lookup is a deliberate boundary, not a substitute for explicit dependencies.
Service Locator: A pattern that gives callers a central place to look up services by key or type instead of receiving those services directly through constructors or method parameters.
In Java, the implementation mechanics are easy. The architectural consequences are harder. That is why a page about implementing Service Locator should show both the shape and the cost.
The minimal design has two distinct phases:
1public final class ServiceLocator {
2 private final Map<Class<?>, Object> services;
3
4 public ServiceLocator(Map<Class<?>, Object> services) {
5 this.services = Map.copyOf(services);
6 }
7
8 public <T> T get(Class<T> type) {
9 Object service = services.get(type);
10 if (service == null) {
11 throw new IllegalArgumentException("No service for " + type.getName());
12 }
13 return type.cast(service);
14 }
15}
This is technically straightforward. The hard part is deciding whether the caller should be allowed to do lookup at all.
Constructor injection makes dependencies visible in the type signature. Service Locator moves them into method bodies:
1public final class InvoiceExporter {
2 private final ServiceLocator locator;
3
4 public InvoiceExporter(ServiceLocator locator) {
5 this.locator = locator;
6 }
7
8 public void export(Invoice invoice) {
9 PdfRenderer renderer = locator.get(PdfRenderer.class);
10 AuditLogger auditLogger = locator.get(AuditLogger.class);
11 renderer.render(invoice);
12 auditLogger.recordExport(invoice.id());
13 }
14}
The object appears to depend only on ServiceLocator, but in reality it also depends on PdfRenderer and AuditLogger. That hidden dependency surface is the pattern’s main risk.
Service Locator is most defensible in Java when:
In those contexts, central lookup can be an honest boundary rather than a shortcut.
As a general application pattern, Service Locator makes code harder to reason about:
That is why modern Java code usually prefers explicit dependency injection for core business objects.
If Service Locator is justified, keep it constrained:
The narrower the lookup surface, the less architectural drift it creates.
When reviewing a Java Service Locator implementation, ask:
Service Locator is a valid pattern only when the lookup boundary is real. It is a smell when the lookup boundary exists only to avoid naming dependencies honestly.