Browse Java Design Patterns & Enterprise Application Architecture

Implementing Service Locator in Java

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 Smallest Useful Shape

The minimal design has two distinct phases:

  • bootstrap registers the available services
  • application code looks them up
 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.

Where The Dependency Becomes Hidden

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.

When It Can Still Be Reasonable

Service Locator is most defensible in Java when:

  • integrating with older frameworks or application servers
  • implementing plugin or extension lookups
  • handling optional infrastructure discovered at runtime
  • adapting a legacy system before a gradual move toward explicit injection

In those contexts, central lookup can be an honest boundary rather than a shortcut.

Why It Is Not The Default Recommendation

As a general application pattern, Service Locator makes code harder to reason about:

  • required dependencies disappear from constructors
  • tests need locator setup just to understand a class
  • missing registrations fail later than missing constructor arguments
  • classes can quietly start pulling in more collaborators over time

That is why modern Java code usually prefers explicit dependency injection for core business objects.

Keep The Locator Narrow

If Service Locator is justified, keep it constrained:

  • own registration at bootstrap, not ad hoc from random code
  • expose only the services relevant to the boundary
  • fail fast on missing registrations
  • avoid using it as a universal global object directory

The narrower the lookup surface, the less architectural drift it creates.

Design Review Questions

When reviewing a Java Service Locator implementation, ask:

  • Why is runtime lookup better than explicit injection here?
  • Which dependencies are now hidden from the constructor?
  • Who owns registration and lifecycle?
  • Is this a stable plugin boundary or just a shortcut around wiring?

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.

Loading quiz…
Revised on Thursday, April 23, 2026