Real code example: Spring boot with OpenFeign client, Hystrix and Spring Service Locator pattern — Part 1

  • Openfeign client to connect to fake APi server JsonPlaceholder.
  • Hystrix command asynchronous execution.
  • Spring service locator pattern for dynamically locating services.

Part 1: Spring service locator

Before going deep into code, I recommend you to read through these article for better understanding this pattern.

  1. Introduction, key differences and small implementation:
    https://www.baeldung.com/java-service-locator-pattern
  2. Introduction, explanation and small implementation:
    https://springframework.guru/service-locator-pattern-in-spring/#:~:text=Introduction%3A%20Service%20Locator%20Pattern&text=Additionally%2C%20Spring%20provides%20a%20form,has%20on%20the%20concrete%20implementation
    .

Legacy service factory

Before using this pattern, we usually using switch/case for selecting services base on input argument(s).

@Service
public class InputParserFactory {
@Autowired
private InputJsonParser jsonParser;
@Autowired
private InputCsvParser csvParser;
public InputParser getParser(InputContentType contentType) {
switch (contentType) {
case JSON:
return jsonParser;
case CSV:
return csvParser;
default:
return jsonParser;
}
}
}

A. Service abstraction:

Firstly, we define a service interface which will define our services abstract method(s). Here is InputParser interface.

public interface InputParser {
List<PostModel> parse(Reader reader);
}

B. Service factory:

Next, we define a service factory interface, which has only one method, to give back service base on content type

public interface InputParserFactory {
public InputParser getParser(InputContentType contentType);
}

C. Service beans:

Finally, we define our real service implementation

@Component(InputContentType.Constant.JSON_TEXT)
@Slf4j
public class InputJsonParser implements InputParser {
...
}
@Component(InputContentType.Constant.CSV_TEXT)
@Slf4j
public class InputCsvParser implements InputParser {
...
}

D. Factory bean:

Last, we need to register our service factory with Spring factory bean

@Configuration
public class AppServiceConfig {
@Bean("parserFactory")
public FactoryBean<Object> inputParserFactory(){
ServiceLocatorFactoryBean factoryBean = new ServiceLocatorFactoryBean();
factoryBean.setServiceLocatorInterface(InputParserFactory.class);
return factoryBean;
}
}

E. Bonus:

In order to haver better control bean name, because we don’t want to define constant String for every bean, later if we want to change name, we have to go to every class and change the constant String.

public enum InputContentType {
JSON(Constant.JSON_TEXT), CSV(Constant.CSV_TEXT);
private String typeName;
private InputContentType(String typeName) {
this.typeName = typeName;
}
@Override
public String toString() {
return this.typeName;
}
public static InputContentType fromContentType(final String contentType) {
return Stream.of(InputContentType.values()).filter(type -> type.toString().equalsIgnoreCase(contentType))
.findFirst()
.orElseThrow(() -> new UnsupportedMediaTypeStatusException("Unsupport conten type " + contentType));
}
public interface Constant {
String JSON_TEXT = MediaType.APPLICATION_JSON_VALUE;
String CSV_TEXT = "text/csv";
}
}

Behind the scene

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store