Backend Development 8 min read

Applying the Service Locator Pattern to Decouple File Parsers in Java

This article explains how to replace tightly‑coupled if‑else or switch‑case logic for selecting file parsers (JSON, CSV, XML) with a Service Locator Pattern using Spring's ServiceLocatorFactoryBean, demonstrating a clean, extensible solution that follows the Open/Closed principle.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Applying the Service Locator Pattern to Decouple File Parsers in Java

Hello everyone, I’m Chen. In many projects we select different implementations (e.g., CSV or JSON parser) based on a type using if else or switch case , which tightly couples the client to concrete classes. This article introduces the Service Locator Pattern to eliminate that coupling.

File Parser Example

We illustrate the pattern with a simple example that parses files of different formats.

Assume an application needs to parse CSV and JSON files.

1. Define an enum for content types:

public enum ContentType {
  JSON,
  CSV
}

2. Define a parser interface:

public interface Parser {
  List parse(Reader r);
}

3. Provide concrete implementations:

// CSV parser
@Component
public class CSVParser implements Parser {
  @Override
  public List parse(Reader r) { .. }
}

// JSON parser
@Component
public class JSONParser implements Parser {
  @Override
  public List parse(Reader r) { .. }
}

4. A naïve client would use a switch case to select the parser:

@Service
public class Client {
  private Parser csvParser, jsonParser;

  @Autowired
  public Client(Parser csvParser, Parser jsonParser) {
    this.csvParser = csvParser;
    this.jsonParser = jsonParser;
  }

  public List getAll(ContentType contentType) {
    switch (contentType) {
      case CSV: return csvParser.parse(reader);
      case JSON: return jsonParser.parse(reader);
      // ...
    }
  }
}

This approach works but requires modifying the client whenever a new type (e.g., XML) is added, violating the Open/Closed principle.

Applying the Service Locator Pattern

We introduce a service locator interface ParserFactory that returns a Parser based on ContentType :

public interface ParserFactory {
  Parser getParser(ContentType contentType);
}

Configure Spring's ServiceLocatorFactoryBean to create the factory:

@Configuration
public class ParserConfig {
  @Bean("parserFactory")
  public FactoryBean serviceLocatorFactoryBean() {
    ServiceLocatorFactoryBean factoryBean = new ServiceLocatorFactoryBean();
    // set the service‑locator interface
    factoryBean.setServiceLocatorInterface(ParserFactory.class);
    return factoryBean;
  }
}

Register each parser bean with a name matching the enum constant:

// Bean name matches enum value
@Component("CSV")
public class CSVParser implements Parser { .. }

@Component("JSON")
public class JSONParser implements Parser { .. }

@Component("XML")
public class XMLParser implements Parser { .. }

Update the enum to include XML:

public enum ContentType {
  JSON,
  CSV,
  XML
}

Now the client can obtain the appropriate parser without a switch:

@Service
public class Client {
  private ParserFactory parserFactory;

  @Autowired
  public Client(ParserFactory parserFactory) {
    this.parserFactory = parserFactory;
  }

  public List getAll(ContentType contentType) {
    // Directly retrieve the parser from the factory
    return parserFactory.getParser(contentType).parse(reader);
  }
}

Adding a new file type only requires creating a new parser bean and adding the enum constant; the client remains unchanged, satisfying the Open/Closed principle.

Recommended Java Engineer Guide: https://github.com/chenjiabing666/JavaFamily

If you find using the type name as the bean name odd, you can use a constant‑based approach:

public enum ContentType {
  JSON(TypeConstants.JSON_PARSER),
  CSV(TypeConstants.CSV_PARSER),
  XML(TypeConstants.XML_PARSER);
  private final String parserName;
  ContentType(String parserName) { this.parserName = parserName; }
  @Override public String toString() { return parserName; }
  public interface TypeConstants {
    String CSV_PARSER = "csvParser";
    String JSON_PARSER = "jsonParser";
    String XML_PARSER = "xmlParser";
  }
}

@Component(TypeConstants.CSV_PARSER)
public class CSVParser implements Parser { .. }
@Component(TypeConstants.JSON_PARSER)
public class JSONParser implements Parser { .. }
@Component(TypeConstants.XML_PARSER)
public class XMLParser implements Parser { .. }

Analysis of the Service Locator Pattern

The pattern removes the client’s direct dependency on concrete implementations. As Martin Fowler notes, a service locator knows how to obtain any service the application may need, providing a method that returns the required service on demand.

Spring’s ServiceLocatorFactoryBean implements FactoryBean to create a service‑factory bean that can locate services at runtime.

Conclusion

Using the Service Locator Pattern offers a flexible way to extend Spring’s inversion of control for cases where dependency injection alone is insufficient, though DI remains the preferred approach in most scenarios.

Final Note

If this article helped you, please like, share, and follow the author. Your support keeps the content coming!

backendjavaspringDesign PatternDependency InjectionService Locator
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.